summaryrefslogtreecommitdiffstats
path: root/gfx/layers/basic
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /gfx/layers/basic
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--gfx/layers/basic/AutoMaskData.h54
-rw-r--r--gfx/layers/basic/BasicCanvasLayer.cpp77
-rw-r--r--gfx/layers/basic/BasicCanvasLayer.h44
-rw-r--r--gfx/layers/basic/BasicColorLayer.cpp75
-rw-r--r--gfx/layers/basic/BasicCompositor.cpp1252
-rw-r--r--gfx/layers/basic/BasicCompositor.h240
-rw-r--r--gfx/layers/basic/BasicContainerLayer.cpp162
-rw-r--r--gfx/layers/basic/BasicContainerLayer.h103
-rw-r--r--gfx/layers/basic/BasicImageLayer.cpp108
-rw-r--r--gfx/layers/basic/BasicImages.cpp180
-rw-r--r--gfx/layers/basic/BasicImplData.h140
-rw-r--r--gfx/layers/basic/BasicLayerManager.cpp969
-rw-r--r--gfx/layers/basic/BasicLayers.h243
-rw-r--r--gfx/layers/basic/BasicLayersImpl.cpp219
-rw-r--r--gfx/layers/basic/BasicLayersImpl.h145
-rw-r--r--gfx/layers/basic/BasicPaintedLayer.cpp239
-rw-r--r--gfx/layers/basic/BasicPaintedLayer.h121
-rw-r--r--gfx/layers/basic/MacIOSurfaceTextureHostBasic.cpp89
-rw-r--r--gfx/layers/basic/MacIOSurfaceTextureHostBasic.h87
-rw-r--r--gfx/layers/basic/TextureClientX11.cpp140
-rw-r--r--gfx/layers/basic/TextureClientX11.h56
-rw-r--r--gfx/layers/basic/TextureHostBasic.cpp33
-rw-r--r--gfx/layers/basic/TextureHostBasic.h38
-rw-r--r--gfx/layers/basic/X11BasicCompositor.cpp116
-rw-r--r--gfx/layers/basic/X11BasicCompositor.h66
-rw-r--r--gfx/layers/basic/X11TextureSourceBasic.cpp48
-rw-r--r--gfx/layers/basic/X11TextureSourceBasic.h50
27 files changed, 5094 insertions, 0 deletions
diff --git a/gfx/layers/basic/AutoMaskData.h b/gfx/layers/basic/AutoMaskData.h
new file mode 100644
index 0000000000..8775c8c33d
--- /dev/null
+++ b/gfx/layers/basic/AutoMaskData.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef GFX_AUTOMASKDATA_H_
+#define GFX_AUTOMASKDATA_H_
+
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * Drawing with a mask requires a mask surface and a transform.
+ *
+ * This helper class manages the SourceSurface logic.
+ */
+class MOZ_STACK_CLASS AutoMoz2DMaskData {
+ public:
+ AutoMoz2DMaskData() = default;
+ ~AutoMoz2DMaskData() = default;
+
+ void Construct(const gfx::Matrix& aTransform, gfx::SourceSurface* aSurface) {
+ MOZ_ASSERT(!IsConstructed());
+ mTransform = aTransform;
+ mSurface = aSurface;
+ }
+
+ gfx::SourceSurface* GetSurface() {
+ MOZ_ASSERT(IsConstructed());
+ return mSurface.get();
+ }
+
+ const gfx::Matrix& GetTransform() {
+ MOZ_ASSERT(IsConstructed());
+ return mTransform;
+ }
+
+ private:
+ bool IsConstructed() { return !!mSurface; }
+
+ gfx::Matrix mTransform;
+ RefPtr<gfx::SourceSurface> mSurface;
+
+ AutoMoz2DMaskData(const AutoMoz2DMaskData&) = delete;
+ AutoMoz2DMaskData& operator=(const AutoMoz2DMaskData&) = delete;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // GFX_AUTOMASKDATA_H_
diff --git a/gfx/layers/basic/BasicCanvasLayer.cpp b/gfx/layers/basic/BasicCanvasLayer.cpp
new file mode 100644
index 0000000000..5f7399c020
--- /dev/null
+++ b/gfx/layers/basic/BasicCanvasLayer.cpp
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "BasicCanvasLayer.h"
+#include "basic/BasicLayers.h" // for BasicLayerManager
+#include "basic/BasicLayersImpl.h" // for GetEffectiveOperator
+#include "CanvasRenderer.h"
+#include "mozilla/mozalloc.h" // for operator new
+#include "mozilla/Maybe.h"
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsISupportsImpl.h" // for Layer::AddRef, etc
+#include "gfx2DGlue.h"
+#include "GLScreenBuffer.h"
+#include "GLContext.h"
+#include "gfxUtils.h"
+#include "mozilla/layers/PersistentBufferProvider.h"
+#include "client/TextureClientSharedSurface.h"
+
+class gfxContext;
+
+using namespace mozilla::gfx;
+using namespace mozilla::gl;
+
+namespace mozilla {
+namespace layers {
+
+void BasicCanvasLayer::Paint(DrawTarget* aDT, const Point& aDeviceOffset,
+ Layer* aMaskLayer) {
+ if (IsHidden()) return;
+ // Ignore IsDirty().
+
+ MOZ_ASSERT(mCanvasRenderer);
+ mCanvasRenderer->FirePreTransactionCallback();
+
+ const auto snapshot = mCanvasRenderer->BorrowSnapshot();
+ if (!snapshot) return;
+ const auto& surface = snapshot->mSurf;
+
+ Maybe<Matrix> oldTM;
+ if (!mCanvasRenderer->YIsDown()) {
+ // y-flip
+ oldTM = Some(aDT->GetTransform());
+ aDT->SetTransform(Matrix(*oldTM)
+ .PreTranslate(0.0f, mBounds.Height())
+ .PreScale(1.0f, -1.0f));
+ }
+
+ FillRectWithMask(
+ aDT, aDeviceOffset, Rect(0, 0, mBounds.Width(), mBounds.Height()),
+ surface, mSamplingFilter,
+ DrawOptions(GetEffectiveOpacity(), GetEffectiveOperator(this)),
+ aMaskLayer);
+
+ if (oldTM) {
+ aDT->SetTransform(*oldTM);
+ }
+
+ mCanvasRenderer->FireDidTransactionCallback();
+
+ Painted();
+}
+
+RefPtr<CanvasRenderer> BasicCanvasLayer::CreateCanvasRendererInternal() {
+ return new CanvasRenderer();
+}
+
+already_AddRefed<CanvasLayer> BasicLayerManager::CreateCanvasLayer() {
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ RefPtr<CanvasLayer> layer = new BasicCanvasLayer(this);
+ return layer.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/basic/BasicCanvasLayer.h b/gfx/layers/basic/BasicCanvasLayer.h
new file mode 100644
index 0000000000..4753acdb58
--- /dev/null
+++ b/gfx/layers/basic/BasicCanvasLayer.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef GFX_BASICCANVASLAYER_H
+#define GFX_BASICCANVASLAYER_H
+
+#include "BasicImplData.h" // for BasicImplData
+#include "BasicLayers.h" // for BasicLayerManager
+#include "Layers.h" // for CanvasLayer, etc
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsRegion.h" // for nsIntRegion
+
+namespace mozilla {
+namespace layers {
+
+class BasicCanvasLayer : public CanvasLayer, public BasicImplData {
+ public:
+ explicit BasicCanvasLayer(BasicLayerManager* aLayerManager)
+ : CanvasLayer(aLayerManager, static_cast<BasicImplData*>(this)) {}
+
+ void SetVisibleRegion(const LayerIntRegion& aRegion) override {
+ NS_ASSERTION(BasicManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ CanvasLayer::SetVisibleRegion(aRegion);
+ }
+
+ void Paint(gfx::DrawTarget* aDT, const gfx::Point& aDeviceOffset,
+ Layer* aMaskLayer) override;
+
+ protected:
+ BasicLayerManager* BasicManager() {
+ return static_cast<BasicLayerManager*>(mManager);
+ }
+
+ RefPtr<CanvasRenderer> CreateCanvasRendererInternal() override;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/basic/BasicColorLayer.cpp b/gfx/layers/basic/BasicColorLayer.cpp
new file mode 100644
index 0000000000..b8a37f7420
--- /dev/null
+++ b/gfx/layers/basic/BasicColorLayer.cpp
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "BasicLayersImpl.h" // for FillRectWithMask, etc
+#include "Layers.h" // for ColorLayer, etc
+#include "BasicImplData.h" // for BasicImplData
+#include "BasicLayers.h" // for BasicLayerManager
+#include "gfxContext.h" // for gfxContext, etc
+#include "gfxRect.h" // for gfxRect
+#include "gfx2DGlue.h"
+#include "mozilla/mozalloc.h" // for operator new
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for Layer::AddRef, etc
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsRegion.h" // for nsIntRegion
+#include "mozilla/gfx/PathHelpers.h"
+
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace layers {
+
+class BasicColorLayer : public ColorLayer, public BasicImplData {
+ public:
+ explicit BasicColorLayer(BasicLayerManager* aLayerManager)
+ : ColorLayer(aLayerManager, static_cast<BasicImplData*>(this)) {
+ MOZ_COUNT_CTOR(BasicColorLayer);
+ }
+
+ protected:
+ MOZ_COUNTED_DTOR_OVERRIDE(BasicColorLayer)
+
+ public:
+ void SetVisibleRegion(const LayerIntRegion& aRegion) override {
+ NS_ASSERTION(BasicManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ ColorLayer::SetVisibleRegion(aRegion);
+ }
+
+ void Paint(DrawTarget* aDT, const gfx::Point& aDeviceOffset,
+ Layer* aMaskLayer) override {
+ if (IsHidden()) {
+ return;
+ }
+
+ Rect snapped(mBounds.X(), mBounds.Y(), mBounds.Width(), mBounds.Height());
+ MaybeSnapToDevicePixels(snapped, *aDT, true);
+
+ // Clip drawing in case we're using (unbounded) operator source.
+ aDT->PushClipRect(snapped);
+ FillRectWithMask(
+ aDT, aDeviceOffset, snapped, mColor,
+ DrawOptions(GetEffectiveOpacity(), GetEffectiveOperator(this)),
+ aMaskLayer);
+ aDT->PopClip();
+ }
+
+ protected:
+ BasicLayerManager* BasicManager() {
+ return static_cast<BasicLayerManager*>(mManager);
+ }
+};
+
+already_AddRefed<ColorLayer> BasicLayerManager::CreateColorLayer() {
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ RefPtr<ColorLayer> layer = new BasicColorLayer(this);
+ return layer.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/basic/BasicCompositor.cpp b/gfx/layers/basic/BasicCompositor.cpp
new file mode 100644
index 0000000000..a058baf21d
--- /dev/null
+++ b/gfx/layers/basic/BasicCompositor.cpp
@@ -0,0 +1,1252 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "BasicCompositor.h"
+#include "BasicLayersImpl.h" // for FillRectWithMask
+#include "GeckoProfiler.h"
+#include "TextureHostBasic.h"
+#include "mozilla/layers/Effects.h"
+#include "nsIWidget.h"
+#include "gfx2DGlue.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/gfx/Helpers.h"
+#include "mozilla/gfx/Swizzle.h"
+#include "mozilla/gfx/Tools.h"
+#include "mozilla/gfx/ssse3-scaler.h"
+#include "mozilla/layers/ImageDataSerializer.h"
+#include "mozilla/layers/NativeLayer.h"
+#include "mozilla/SSE.h"
+#include "mozilla/StaticPrefs_layers.h"
+#include "mozilla/StaticPrefs_nglayout.h"
+#include "gfxPlatform.h"
+#include "gfxUtils.h"
+#include "YCbCrUtils.h"
+#include <algorithm>
+#include "ImageContainer.h"
+#ifdef MOZ_WIDGET_GTK
+# include "mozilla/WidgetUtilsGtk.h"
+#endif
+
+namespace mozilla {
+using namespace mozilla::gfx;
+
+namespace layers {
+
+class DataTextureSourceBasic : public DataTextureSource,
+ public TextureSourceBasic {
+ public:
+ const char* Name() const override { return "DataTextureSourceBasic"; }
+
+ explicit DataTextureSourceBasic(DataSourceSurface* aSurface)
+ : mSurface(aSurface), mWrappingExistingData(!!aSurface) {}
+
+ DataTextureSource* AsDataTextureSource() override {
+ // If the texture wraps someone else's memory we'd rather not use it as
+ // a DataTextureSource per say (that is call Update on it).
+ return mWrappingExistingData ? nullptr : this;
+ }
+
+ TextureSourceBasic* AsSourceBasic() override { return this; }
+
+ gfx::SourceSurface* GetSurface(DrawTarget* aTarget) override {
+ return mSurface;
+ }
+
+ SurfaceFormat GetFormat() const override {
+ return mSurface ? mSurface->GetFormat() : gfx::SurfaceFormat::UNKNOWN;
+ }
+
+ IntSize GetSize() const override {
+ return mSurface ? mSurface->GetSize() : gfx::IntSize(0, 0);
+ }
+
+ bool Update(gfx::DataSourceSurface* aSurface,
+ nsIntRegion* aDestRegion = nullptr,
+ gfx::IntPoint* aSrcOffset = nullptr) override {
+ MOZ_ASSERT(!mWrappingExistingData);
+ if (mWrappingExistingData) {
+ return false;
+ }
+ mSurface = aSurface;
+ return true;
+ }
+
+ void DeallocateDeviceData() override {
+ mSurface = nullptr;
+ SetUpdateSerial(0);
+ }
+
+ public:
+ RefPtr<gfx::DataSourceSurface> mSurface;
+ bool mWrappingExistingData;
+};
+
+/**
+ * WrappingTextureSourceYCbCrBasic wraps YUV format BufferTextureHost to defer
+ * yuv->rgb conversion. The conversion happens when GetSurface is called.
+ */
+class WrappingTextureSourceYCbCrBasic : public DataTextureSource,
+ public TextureSourceBasic {
+ public:
+ const char* Name() const override {
+ return "WrappingTextureSourceYCbCrBasic";
+ }
+
+ explicit WrappingTextureSourceYCbCrBasic(BufferTextureHost* aTexture)
+ : mTexture(aTexture), mSize(aTexture->GetSize()), mNeedsUpdate(true) {
+ mFromYCBCR = true;
+ }
+
+ DataTextureSource* AsDataTextureSource() override { return this; }
+
+ TextureSourceBasic* AsSourceBasic() override { return this; }
+
+ WrappingTextureSourceYCbCrBasic* AsWrappingTextureSourceYCbCrBasic()
+ override {
+ return this;
+ }
+
+ gfx::SourceSurface* GetSurface(DrawTarget* aTarget) override {
+ if (mSurface && !mNeedsUpdate) {
+ return mSurface;
+ }
+ MOZ_ASSERT(mTexture);
+ if (!mTexture) {
+ return nullptr;
+ }
+
+ if (!mSurface) {
+ mSurface =
+ Factory::CreateDataSourceSurface(mSize, gfx::SurfaceFormat::B8G8R8X8);
+ }
+ if (!mSurface) {
+ return nullptr;
+ }
+ MOZ_ASSERT(mTexture->GetBufferDescriptor().type() ==
+ BufferDescriptor::TYCbCrDescriptor);
+ MOZ_ASSERT(mTexture->GetSize() == mSize);
+
+ mSurface = ImageDataSerializer::DataSourceSurfaceFromYCbCrDescriptor(
+ mTexture->GetBuffer(),
+ mTexture->GetBufferDescriptor().get_YCbCrDescriptor(), mSurface);
+ mNeedsUpdate = false;
+ return mSurface;
+ }
+
+ SurfaceFormat GetFormat() const override {
+ return gfx::SurfaceFormat::B8G8R8X8;
+ }
+
+ IntSize GetSize() const override { return mSize; }
+
+ virtual bool Update(gfx::DataSourceSurface* aSurface,
+ nsIntRegion* aDestRegion = nullptr,
+ gfx::IntPoint* aSrcOffset = nullptr) override {
+ return false;
+ }
+
+ void DeallocateDeviceData() override {
+ mTexture = nullptr;
+ mSurface = nullptr;
+ SetUpdateSerial(0);
+ }
+
+ void Unbind() override { mNeedsUpdate = true; }
+
+ void SetBufferTextureHost(BufferTextureHost* aTexture) override {
+ mTexture = aTexture;
+ mNeedsUpdate = true;
+ }
+
+ void ConvertAndScale(const SurfaceFormat& aDestFormat,
+ const IntSize& aDestSize, unsigned char* aDestBuffer,
+ int32_t aStride) {
+ MOZ_ASSERT(mTexture);
+ if (!mTexture) {
+ return;
+ }
+ MOZ_ASSERT(mTexture->GetBufferDescriptor().type() ==
+ BufferDescriptor::TYCbCrDescriptor);
+ MOZ_ASSERT(mTexture->GetSize() == mSize);
+ ImageDataSerializer::ConvertAndScaleFromYCbCrDescriptor(
+ mTexture->GetBuffer(),
+ mTexture->GetBufferDescriptor().get_YCbCrDescriptor(), aDestFormat,
+ aDestSize, aDestBuffer, aStride);
+ }
+
+ public:
+ BufferTextureHost* mTexture;
+ const gfx::IntSize mSize;
+ RefPtr<gfx::DataSourceSurface> mSurface;
+ bool mNeedsUpdate;
+};
+
+class BasicAsyncReadbackBuffer final : public AsyncReadbackBuffer {
+ public:
+ explicit BasicAsyncReadbackBuffer(const IntSize& aSize)
+ : AsyncReadbackBuffer(aSize) {}
+
+ bool MapAndCopyInto(DataSourceSurface* aSurface,
+ const IntSize& aReadSize) const override;
+
+ void TakeSurface(SourceSurface* aSurface) { mSurface = aSurface; }
+
+ private:
+ RefPtr<SourceSurface> mSurface;
+};
+
+bool BasicAsyncReadbackBuffer::MapAndCopyInto(DataSourceSurface* aSurface,
+ const IntSize& aReadSize) const {
+ if (!mSurface) {
+ return false;
+ }
+
+ MOZ_RELEASE_ASSERT(aReadSize <= aSurface->GetSize());
+ RefPtr<DataSourceSurface> source = mSurface->GetDataSurface();
+
+ DataSourceSurface::ScopedMap sourceMap(source, DataSourceSurface::READ);
+ DataSourceSurface::ScopedMap destMap(aSurface, DataSourceSurface::WRITE);
+
+ return SwizzleData(sourceMap.GetData(), sourceMap.GetStride(),
+ mSurface->GetFormat(), destMap.GetData(),
+ destMap.GetStride(), aSurface->GetFormat(), aReadSize);
+}
+
+BasicCompositor::BasicCompositor(CompositorBridgeParent* aParent,
+ widget::CompositorWidget* aWidget)
+ : Compositor(aWidget, aParent),
+ mIsPendingEndRemoteDrawing(false),
+ mFullWindowRenderTarget(nullptr) {
+ MOZ_COUNT_CTOR(BasicCompositor);
+
+ // The widget backends may create intermediate Cairo surfaces to deal with
+ // various window buffers, regardless of actual content backend type, when
+ // using the basic compositor. Ensure that the buffers will be able to fit
+ // in or blit with a Cairo surface.
+ mMaxTextureSize =
+ std::min(Factory::GetMaxSurfaceSize(gfxVars::ContentBackend()),
+ Factory::GetMaxSurfaceSize(BackendType::CAIRO));
+}
+
+BasicCompositor::~BasicCompositor() { MOZ_COUNT_DTOR(BasicCompositor); }
+
+bool BasicCompositor::Initialize(nsCString* const out_failureReason) {
+ return mWidget ? mWidget->InitCompositor(this) : false;
+};
+
+int32_t BasicCompositor::GetMaxTextureSize() const { return mMaxTextureSize; }
+
+void BasicCompositingRenderTarget::BindRenderTarget() {
+ if (mClearOnBind) {
+ mDrawTarget->ClearRect(Rect(GetRect()));
+ mClearOnBind = false;
+ }
+}
+
+void BasicCompositor::Destroy() {
+ if (mIsPendingEndRemoteDrawing) {
+ // Force to end previous remote drawing.
+ EndRemoteDrawing();
+ MOZ_ASSERT(!mIsPendingEndRemoteDrawing);
+ }
+ mWidget->CleanupRemoteDrawing();
+
+ Compositor::Destroy();
+}
+
+TextureFactoryIdentifier BasicCompositor::GetTextureFactoryIdentifier() {
+ TextureFactoryIdentifier ident(LayersBackend::LAYERS_BASIC,
+ XRE_GetProcessType(), GetMaxTextureSize());
+ return ident;
+}
+
+already_AddRefed<CompositingRenderTarget> BasicCompositor::CreateRenderTarget(
+ const IntRect& aRect, SurfaceInitMode aInit) {
+ MOZ_ASSERT(!aRect.IsZeroArea(),
+ "Trying to create a render target of invalid size");
+
+ if (aRect.IsZeroArea()) {
+ return nullptr;
+ }
+
+ RefPtr<DrawTarget> target =
+ mRenderTarget->mDrawTarget->CreateSimilarDrawTarget(
+ aRect.Size(), SurfaceFormat::B8G8R8A8);
+
+ if (!target) {
+ return nullptr;
+ }
+
+ RefPtr<BasicCompositingRenderTarget> rt =
+ new BasicCompositingRenderTarget(target, aRect, aRect.TopLeft());
+
+ rt->mDrawTarget->SetTransform(Matrix::Translation(-rt->GetOrigin()));
+
+ return rt.forget();
+}
+
+already_AddRefed<CompositingRenderTarget>
+BasicCompositor::CreateRenderTargetFromSource(
+ const IntRect& aRect, const CompositingRenderTarget* aSource,
+ const IntPoint& aSourcePoint) {
+ MOZ_CRASH("GFX: Shouldn't be called!");
+ return nullptr;
+}
+
+already_AddRefed<CompositingRenderTarget>
+BasicCompositor::CreateRootRenderTarget(DrawTarget* aDrawTarget,
+ const IntRect& aDrawTargetRect,
+ const IntRegion& aClearRegion) {
+ RefPtr<BasicCompositingRenderTarget> rt = new BasicCompositingRenderTarget(
+ aDrawTarget, aDrawTargetRect, IntPoint());
+
+ rt->mDrawTarget->SetTransform(Matrix::Translation(-rt->GetOrigin()));
+
+ if (!aClearRegion.IsEmpty()) {
+ gfx::IntRect clearRect = aClearRegion.GetBounds();
+ gfxUtils::ClipToRegion(rt->mDrawTarget, aClearRegion);
+ rt->mDrawTarget->ClearRect(gfx::Rect(clearRect));
+ rt->mDrawTarget->PopClip();
+ }
+
+ return rt.forget();
+}
+
+already_AddRefed<DataTextureSource> BasicCompositor::CreateDataTextureSource(
+ TextureFlags aFlags) {
+ RefPtr<DataTextureSourceBasic> result = new DataTextureSourceBasic(nullptr);
+ if (aFlags & TextureFlags::RGB_FROM_YCBCR) {
+ result->mFromYCBCR = true;
+ }
+ return result.forget();
+}
+
+already_AddRefed<DataTextureSource>
+BasicCompositor::CreateDataTextureSourceAround(DataSourceSurface* aSurface) {
+ RefPtr<DataTextureSource> result = new DataTextureSourceBasic(aSurface);
+ return result.forget();
+}
+
+already_AddRefed<DataTextureSource>
+BasicCompositor::CreateDataTextureSourceAroundYCbCr(TextureHost* aTexture) {
+ BufferTextureHost* bufferTexture = aTexture->AsBufferTextureHost();
+ MOZ_ASSERT(bufferTexture);
+
+ if (!bufferTexture) {
+ return nullptr;
+ }
+ RefPtr<DataTextureSource> result =
+ new WrappingTextureSourceYCbCrBasic(bufferTexture);
+ return result.forget();
+}
+
+bool BasicCompositor::SupportsEffect(EffectTypes aEffect) {
+ return aEffect != EffectTypes::YCBCR &&
+ aEffect != EffectTypes::COMPONENT_ALPHA;
+}
+
+bool BasicCompositor::SupportsLayerGeometry() const {
+ return StaticPrefs::layers_geometry_basic_enabled();
+}
+
+static RefPtr<gfx::Path> BuildPathFromPolygon(const RefPtr<DrawTarget>& aDT,
+ const gfx::Polygon& aPolygon) {
+ MOZ_ASSERT(!aPolygon.IsEmpty());
+
+ RefPtr<PathBuilder> pathBuilder = aDT->CreatePathBuilder();
+ const nsTArray<Point4D>& points = aPolygon.GetPoints();
+
+ pathBuilder->MoveTo(points[0].As2DPoint());
+
+ for (size_t i = 1; i < points.Length(); ++i) {
+ pathBuilder->LineTo(points[i].As2DPoint());
+ }
+
+ pathBuilder->Close();
+ return pathBuilder->Finish();
+}
+
+static void DrawSurface(gfx::DrawTarget* aDest, const gfx::Rect& aDestRect,
+ const gfx::Rect& /* aClipRect */,
+ const gfx::DeviceColor& aColor,
+ const gfx::DrawOptions& aOptions,
+ gfx::SourceSurface* aMask,
+ const gfx::Matrix* aMaskTransform) {
+ FillRectWithMask(aDest, aDestRect, aColor, aOptions, aMask, aMaskTransform);
+}
+
+static void DrawSurface(gfx::DrawTarget* aDest, const gfx::Polygon& aPolygon,
+ const gfx::Rect& aClipRect,
+ const gfx::DeviceColor& aColor,
+ const gfx::DrawOptions& aOptions,
+ gfx::SourceSurface* aMask,
+ const gfx::Matrix* aMaskTransform) {
+ RefPtr<Path> path = BuildPathFromPolygon(aDest, aPolygon);
+ FillPathWithMask(aDest, path, aClipRect, aColor, aOptions, aMask,
+ aMaskTransform);
+}
+
+static void DrawTextureSurface(
+ gfx::DrawTarget* aDest, const gfx::Rect& aDestRect,
+ const gfx::Rect& /* aClipRect */, gfx::SourceSurface* aSource,
+ gfx::SamplingFilter aSamplingFilter, const gfx::DrawOptions& aOptions,
+ ExtendMode aExtendMode, gfx::SourceSurface* aMask,
+ const gfx::Matrix* aMaskTransform, const Matrix* aSurfaceTransform) {
+ FillRectWithMask(aDest, aDestRect, aSource, aSamplingFilter, aOptions,
+ aExtendMode, aMask, aMaskTransform, aSurfaceTransform);
+}
+
+static void DrawTextureSurface(
+ gfx::DrawTarget* aDest, const gfx::Polygon& aPolygon,
+ const gfx::Rect& aClipRect, gfx::SourceSurface* aSource,
+ gfx::SamplingFilter aSamplingFilter, const gfx::DrawOptions& aOptions,
+ ExtendMode aExtendMode, gfx::SourceSurface* aMask,
+ const gfx::Matrix* aMaskTransform, const Matrix* aSurfaceTransform) {
+ RefPtr<Path> path = BuildPathFromPolygon(aDest, aPolygon);
+ FillPathWithMask(aDest, path, aClipRect, aSource, aSamplingFilter, aOptions,
+ aExtendMode, aMask, aMaskTransform, aSurfaceTransform);
+}
+
+template <typename Geometry>
+static void DrawSurfaceWithTextureCoords(
+ gfx::DrawTarget* aDest, const Geometry& aGeometry,
+ const gfx::Rect& aDestRect, gfx::SourceSurface* aSource,
+ const gfx::Rect& aTextureCoords, gfx::SamplingFilter aSamplingFilter,
+ const gfx::DrawOptions& aOptions, gfx::SourceSurface* aMask,
+ const gfx::Matrix* aMaskTransform) {
+ if (!aSource) {
+ gfxWarning() << "DrawSurfaceWithTextureCoords problem "
+ << gfx::hexa(aSource) << " and " << gfx::hexa(aMask);
+ return;
+ }
+
+ // Convert aTextureCoords into aSource's coordinate space
+ gfxRect sourceRect(aTextureCoords.X() * aSource->GetSize().width,
+ aTextureCoords.Y() * aSource->GetSize().height,
+ aTextureCoords.Width() * aSource->GetSize().width,
+ aTextureCoords.Height() * aSource->GetSize().height);
+
+ // Floating point error can accumulate above and we know our visible region
+ // is integer-aligned, so round it out.
+ sourceRect.Round();
+
+ // Compute a transform that maps sourceRect to aDestRect.
+ Matrix matrix = gfxUtils::TransformRectToRect(
+ sourceRect, gfx::IntPoint::Truncate(aDestRect.X(), aDestRect.Y()),
+ gfx::IntPoint::Truncate(aDestRect.XMost(), aDestRect.Y()),
+ gfx::IntPoint::Truncate(aDestRect.XMost(), aDestRect.YMost()));
+
+ // Only use REPEAT if aTextureCoords is outside (0, 0, 1, 1).
+ gfx::Rect unitRect(0, 0, 1, 1);
+ ExtendMode mode = unitRect.Contains(aTextureCoords) ? ExtendMode::CLAMP
+ : ExtendMode::REPEAT;
+
+ DrawTextureSurface(aDest, aGeometry, aDestRect, aSource, aSamplingFilter,
+ aOptions, mode, aMask, aMaskTransform, &matrix);
+}
+
+static void SetupMask(const EffectChain& aEffectChain, DrawTarget* aDest,
+ const IntPoint& aOffset,
+ RefPtr<SourceSurface>& aMaskSurface,
+ Matrix& aMaskTransform) {
+ if (aEffectChain.mSecondaryEffects[EffectTypes::MASK]) {
+ EffectMask* effectMask = static_cast<EffectMask*>(
+ aEffectChain.mSecondaryEffects[EffectTypes::MASK].get());
+ aMaskSurface = effectMask->mMaskTexture->AsSourceBasic()->GetSurface(aDest);
+ if (!aMaskSurface) {
+ gfxWarning() << "Invalid sourceMask effect";
+ }
+ MOZ_ASSERT(effectMask->mMaskTransform.Is2D(),
+ "How did we end up with a 3D transform here?!");
+ aMaskTransform = effectMask->mMaskTransform.As2D();
+ aMaskTransform.PostTranslate(-aOffset.x, -aOffset.y);
+ }
+}
+
+static bool AttemptVideoScale(TextureSourceBasic* aSource,
+ const SourceSurface* aSourceMask,
+ gfx::Float aOpacity, CompositionOp aBlendMode,
+ const TexturedEffect* aTexturedEffect,
+ const Matrix& aNewTransform,
+ const gfx::Rect& aRect,
+ const gfx::Rect& aClipRect, DrawTarget* aDest,
+ const DrawTarget* aBuffer) {
+#ifdef MOZILLA_SSE_HAVE_CPUID_DETECTION
+ if (!mozilla::supports_ssse3()) return false;
+ if (aNewTransform
+ .IsTranslation()) // unscaled painting should take the regular path
+ return false;
+ if (aNewTransform.HasNonAxisAlignedTransform() ||
+ aNewTransform.HasNegativeScaling())
+ return false;
+ if (aSourceMask || aOpacity != 1.0f) return false;
+ if (aBlendMode != CompositionOp::OP_OVER &&
+ aBlendMode != CompositionOp::OP_SOURCE)
+ return false;
+
+ IntRect dstRect;
+ // the compiler should know a lot about aNewTransform at this point
+ // maybe it can do some sophisticated optimization of the following
+ if (!aNewTransform.TransformBounds(aRect).ToIntRect(&dstRect)) return false;
+
+ IntRect clipRect;
+ if (!aClipRect.ToIntRect(&clipRect)) return false;
+
+ if (!(aTexturedEffect->mTextureCoords == Rect(0.0f, 0.0f, 1.0f, 1.0f)))
+ return false;
+ if (aDest->GetFormat() == SurfaceFormat::R5G6B5_UINT16) return false;
+
+ uint8_t* dstData;
+ IntSize dstSize;
+ int32_t dstStride;
+ SurfaceFormat dstFormat;
+ if (aDest->LockBits(&dstData, &dstSize, &dstStride, &dstFormat)) {
+ // If we're not painting to aBuffer the clip will
+ // be applied later
+ IntRect fillRect = dstRect;
+ if (aDest == aBuffer) {
+ // we need to clip fillRect because LockBits ignores the clip on the aDest
+ fillRect = fillRect.Intersect(clipRect);
+ }
+
+ fillRect = fillRect.Intersect(IntRect(IntPoint(0, 0), aDest->GetSize()));
+ IntPoint offset = fillRect.TopLeft() - dstRect.TopLeft();
+
+ RefPtr<DataSourceSurface> srcSource =
+ aSource->GetSurface(aDest)->GetDataSurface();
+ DataSourceSurface::ScopedMap mapSrc(srcSource, DataSourceSurface::READ);
+
+ bool success = ssse3_scale_data(
+ (uint32_t*)mapSrc.GetData(), srcSource->GetSize().width,
+ srcSource->GetSize().height, mapSrc.GetStride() / 4,
+ ((uint32_t*)dstData) + fillRect.X() + (dstStride / 4) * fillRect.Y(),
+ dstRect.Width(), dstRect.Height(), dstStride / 4, offset.x, offset.y,
+ fillRect.Width(), fillRect.Height());
+
+ aDest->ReleaseBits(dstData);
+ return success;
+ } else
+#endif // MOZILLA_SSE_HAVE_CPUID_DETECTION
+ return false;
+}
+
+static bool AttemptVideoConvertAndScale(
+ TextureSource* aSource, const SourceSurface* aSourceMask,
+ gfx::Float aOpacity, CompositionOp aBlendMode,
+ const TexturedEffect* aTexturedEffect, const Matrix& aNewTransform,
+ const gfx::Rect& aRect, const gfx::Rect& aClipRect, DrawTarget* aDest,
+ const DrawTarget* aBuffer) {
+#if defined(XP_WIN) && defined(_M_X64)
+ // libyuv does not support SIMD scaling on win 64bit. See Bug 1295927.
+ return false;
+#endif
+
+ WrappingTextureSourceYCbCrBasic* wrappingSource =
+ aSource->AsWrappingTextureSourceYCbCrBasic();
+ if (!wrappingSource) return false;
+#ifdef MOZILLA_SSE_HAVE_CPUID_DETECTION
+ if (!mozilla::supports_ssse3()) // libyuv requests SSSE3 for fast YUV
+ // conversion.
+ return false;
+ if (aNewTransform.HasNonAxisAlignedTransform() ||
+ aNewTransform.HasNegativeScaling())
+ return false;
+ if (aSourceMask || aOpacity != 1.0f) return false;
+ if (aBlendMode != CompositionOp::OP_OVER &&
+ aBlendMode != CompositionOp::OP_SOURCE)
+ return false;
+
+ IntRect dstRect;
+ // the compiler should know a lot about aNewTransform at this point
+ // maybe it can do some sophisticated optimization of the following
+ if (!aNewTransform.TransformBounds(aRect).ToIntRect(&dstRect)) return false;
+
+ IntRect clipRect;
+ if (!aClipRect.ToIntRect(&clipRect)) return false;
+
+ if (!(aTexturedEffect->mTextureCoords == Rect(0.0f, 0.0f, 1.0f, 1.0f)))
+ return false;
+ if (aDest->GetFormat() == SurfaceFormat::R5G6B5_UINT16) return false;
+
+ if (aDest == aBuffer && !clipRect.Contains(dstRect)) return false;
+ if (!IntRect(IntPoint(0, 0), aDest->GetSize()).Contains(dstRect))
+ return false;
+
+ uint8_t* dstData;
+ IntSize dstSize;
+ int32_t dstStride;
+ SurfaceFormat dstFormat;
+ if (aDest->LockBits(&dstData, &dstSize, &dstStride, &dstFormat)) {
+ wrappingSource->ConvertAndScale(
+ dstFormat, dstRect.Size(),
+ dstData + ptrdiff_t(dstRect.X()) * BytesPerPixel(dstFormat) +
+ ptrdiff_t(dstRect.Y()) * dstStride,
+ dstStride);
+ aDest->ReleaseBits(dstData);
+# ifdef MOZ_WIDGET_GTK
+ if (mozilla::widget::IsMainWindowTransparent()) {
+ gfx::Rect rect(dstRect.X(), dstRect.Y(), dstRect.Width(),
+ dstRect.Height());
+ aDest->FillRect(rect, ColorPattern(DeviceColor(0, 0, 0, 1)),
+ DrawOptions(1.f, CompositionOp::OP_ADD));
+ aDest->Flush();
+ }
+# endif
+ return true;
+ } else
+#endif // MOZILLA_SSE_HAVE_CPUID_DETECTION
+ return false;
+}
+
+void BasicCompositor::DrawQuad(const gfx::Rect& aRect,
+ const gfx::IntRect& aClipRect,
+ const EffectChain& aEffectChain,
+ gfx::Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect) {
+ DrawGeometry(aRect, aRect, aClipRect, aEffectChain, aOpacity, aTransform,
+ aVisibleRect, true);
+}
+
+void BasicCompositor::DrawPolygon(const gfx::Polygon& aPolygon,
+ const gfx::Rect& aRect,
+ const gfx::IntRect& aClipRect,
+ const EffectChain& aEffectChain,
+ gfx::Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect) {
+ DrawGeometry(aPolygon, aRect, aClipRect, aEffectChain, aOpacity, aTransform,
+ aVisibleRect, false);
+}
+
+template <typename Geometry>
+void BasicCompositor::DrawGeometry(
+ const Geometry& aGeometry, const gfx::Rect& aRect,
+ const gfx::IntRect& aClipRect, const EffectChain& aEffectChain,
+ gfx::Float aOpacity, const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect, const bool aEnableAA) {
+ RefPtr<DrawTarget> buffer = mRenderTarget->mDrawTarget;
+ AutoRestoreTransform autoRestoreTransform(buffer);
+
+ Matrix newTransform;
+ Rect transformBounds;
+ Matrix4x4 new3DTransform;
+ IntPoint offset = mRenderTarget->GetOrigin();
+
+ // For 2D drawing, |dest| and |buffer| are the same surface. For 3D drawing,
+ // |dest| is a temporary surface.
+ RefPtr<DrawTarget> dest;
+
+ if (aTransform.Is2D()) {
+ dest = buffer;
+ newTransform = aTransform.As2D();
+ } else {
+ // Create a temporary surface for the transform.
+ dest = Factory::CreateDrawTarget(gfxVars::ContentBackend(),
+ RoundedOut(aRect).Size(),
+ SurfaceFormat::B8G8R8A8);
+ if (!dest) {
+ return;
+ }
+
+ dest->SetTransform(Matrix::Translation(-aRect.X(), -aRect.Y()));
+
+ // Get the bounds post-transform.
+ transformBounds = aTransform.TransformAndClipBounds(
+ aRect, Rect(mRenderTarget->GetRect()));
+ transformBounds.RoundOut();
+
+ if (transformBounds.IsEmpty()) {
+ return;
+ }
+
+ newTransform = Matrix();
+
+ // When we apply the 3D transformation, we do it against a temporary
+ // surface, so undo the coordinate offset.
+ new3DTransform = aTransform;
+ new3DTransform.PreTranslate(aRect.X(), aRect.Y(), 0);
+ }
+
+ // The current transform on buffer is always only a translation by `-offset`.
+ // aClipRect is relative to mRenderTarget->GetClipSpaceOrigin().
+ // For non-root render targets, the clip space origin is equal to `offset`.
+ // For the root render target, the clip space origin is at (0, 0) and the
+ // offset can be anywhere.
+ IntRect clipRectInRenderTargetSpace =
+ aClipRect + mRenderTarget->GetClipSpaceOrigin();
+ if (Maybe<IntRect> rtClip = mRenderTarget->GetClipRect()) {
+ clipRectInRenderTargetSpace =
+ clipRectInRenderTargetSpace.Intersect(*rtClip);
+ }
+ buffer->PushClipRect(Rect(clipRectInRenderTargetSpace));
+ Rect deviceSpaceClipRect(clipRectInRenderTargetSpace - offset);
+
+ newTransform.PostTranslate(-offset.x, -offset.y);
+ buffer->SetTransform(newTransform);
+
+ RefPtr<SourceSurface> sourceMask;
+ Matrix maskTransform;
+ if (aTransform.Is2D()) {
+ SetupMask(aEffectChain, dest, offset, sourceMask, maskTransform);
+ }
+
+ CompositionOp blendMode = CompositionOp::OP_OVER;
+ if (Effect* effect =
+ aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE].get()) {
+ blendMode = static_cast<EffectBlendMode*>(effect)->mBlendMode;
+ }
+
+ const AntialiasMode aaMode =
+ aEnableAA ? AntialiasMode::DEFAULT : AntialiasMode::NONE;
+
+ DrawOptions drawOptions(aOpacity, blendMode, aaMode);
+
+ switch (aEffectChain.mPrimaryEffect->mType) {
+ case EffectTypes::SOLID_COLOR: {
+ EffectSolidColor* effectSolidColor =
+ static_cast<EffectSolidColor*>(aEffectChain.mPrimaryEffect.get());
+
+ bool unboundedOp = !IsOperatorBoundByMask(blendMode);
+ if (unboundedOp) {
+ dest->PushClipRect(aRect);
+ }
+
+ DrawSurface(dest, aGeometry, aRect, effectSolidColor->mColor, drawOptions,
+ sourceMask, &maskTransform);
+
+ if (unboundedOp) {
+ dest->PopClip();
+ }
+ break;
+ }
+ case EffectTypes::RGB: {
+ TexturedEffect* texturedEffect =
+ static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());
+ TextureSourceBasic* source = texturedEffect->mTexture->AsSourceBasic();
+
+ if (source && texturedEffect->mPremultiplied) {
+ // we have a fast path for video here
+ if (source->mFromYCBCR &&
+ AttemptVideoConvertAndScale(texturedEffect->mTexture, sourceMask,
+ aOpacity, blendMode, texturedEffect,
+ newTransform, aRect,
+ deviceSpaceClipRect, dest, buffer)) {
+ // we succeeded in convert and scaling
+ } else if (source->mFromYCBCR && !source->GetSurface(dest)) {
+ gfxWarning() << "Failed to get YCbCr to rgb surface.";
+ } else if (source->mFromYCBCR &&
+ AttemptVideoScale(source, sourceMask, aOpacity, blendMode,
+ texturedEffect, newTransform, aRect,
+ deviceSpaceClipRect, dest, buffer)) {
+ // we succeeded in scaling
+ } else {
+ DrawSurfaceWithTextureCoords(
+ dest, aGeometry, aRect, source->GetSurface(dest),
+ texturedEffect->mTextureCoords, texturedEffect->mSamplingFilter,
+ drawOptions, sourceMask, &maskTransform);
+ }
+ } else if (source) {
+ SourceSurface* srcSurf = source->GetSurface(dest);
+ if (srcSurf) {
+ RefPtr<DataSourceSurface> srcData = srcSurf->GetDataSurface();
+
+ // Yes, we re-create the premultiplied data every time.
+ // This might be better with a cache, eventually.
+ RefPtr<DataSourceSurface> premultData =
+ gfxUtils::CreatePremultipliedDataSurface(srcData);
+
+ DrawSurfaceWithTextureCoords(dest, aGeometry, aRect, premultData,
+ texturedEffect->mTextureCoords,
+ texturedEffect->mSamplingFilter,
+ drawOptions, sourceMask, &maskTransform);
+ }
+ } else {
+ gfxDevCrash(LogReason::IncompatibleBasicTexturedEffect)
+ << "Bad for basic with " << texturedEffect->mTexture->Name()
+ << " and " << gfx::hexa(sourceMask);
+ }
+
+ break;
+ }
+ case EffectTypes::YCBCR: {
+ MOZ_CRASH("Can't (easily) support component alpha with BasicCompositor!");
+ break;
+ }
+ case EffectTypes::RENDER_TARGET: {
+ EffectRenderTarget* effectRenderTarget =
+ static_cast<EffectRenderTarget*>(aEffectChain.mPrimaryEffect.get());
+ RefPtr<BasicCompositingRenderTarget> surface =
+ static_cast<BasicCompositingRenderTarget*>(
+ effectRenderTarget->mRenderTarget.get());
+ RefPtr<SourceSurface> sourceSurf = surface->mDrawTarget->Snapshot();
+
+ DrawSurfaceWithTextureCoords(dest, aGeometry, aRect, sourceSurf,
+ effectRenderTarget->mTextureCoords,
+ effectRenderTarget->mSamplingFilter,
+ drawOptions, sourceMask, &maskTransform);
+ break;
+ }
+ case EffectTypes::COMPONENT_ALPHA: {
+ MOZ_CRASH("Can't (easily) support component alpha with BasicCompositor!");
+ break;
+ }
+ default: {
+ MOZ_CRASH("Invalid effect type!");
+ break;
+ }
+ }
+
+ if (!aTransform.Is2D()) {
+ dest->Flush();
+
+ RefPtr<SourceSurface> destSnapshot = dest->Snapshot();
+
+ SetupMask(aEffectChain, buffer, offset, sourceMask, maskTransform);
+
+ if (sourceMask) {
+ RefPtr<DrawTarget> transformDT = dest->CreateSimilarDrawTarget(
+ IntSize::Truncate(transformBounds.Width(), transformBounds.Height()),
+ SurfaceFormat::B8G8R8A8);
+ new3DTransform.PostTranslate(-transformBounds.X(), -transformBounds.Y(),
+ 0);
+ if (transformDT &&
+ transformDT->Draw3DTransformedSurface(destSnapshot, new3DTransform)) {
+ RefPtr<SourceSurface> transformSnapshot = transformDT->Snapshot();
+
+ // Transform the source by it's normal transform, and then the inverse
+ // of the mask transform so that it's in the mask's untransformed
+ // coordinate space.
+ Matrix sourceTransform = newTransform;
+ sourceTransform.PostTranslate(transformBounds.TopLeft());
+
+ Matrix inverseMask = maskTransform;
+ inverseMask.Invert();
+
+ sourceTransform *= inverseMask;
+
+ SurfacePattern source(transformSnapshot, ExtendMode::CLAMP,
+ sourceTransform);
+
+ buffer->PushClipRect(transformBounds);
+
+ // Mask in the untransformed coordinate space, and then transform
+ // by the mask transform to put the result back into destination
+ // coords.
+ buffer->SetTransform(maskTransform);
+ buffer->MaskSurface(source, sourceMask, Point(0, 0));
+
+ buffer->PopClip();
+ }
+ } else {
+ buffer->Draw3DTransformedSurface(destSnapshot, new3DTransform);
+ }
+ }
+
+ buffer->PopClip();
+}
+
+void BasicCompositor::ClearRect(const gfx::Rect& aRect) {
+ mRenderTarget->mDrawTarget->ClearRect(aRect);
+
+ if (mFullWindowRenderTarget) {
+ mFullWindowRenderTarget->mDrawTarget->ClearRect(aRect);
+ }
+}
+
+bool BasicCompositor::ReadbackRenderTarget(CompositingRenderTarget* aSource,
+ AsyncReadbackBuffer* aDest) {
+ RefPtr<SourceSurface> snapshot =
+ static_cast<BasicCompositingRenderTarget*>(aSource)
+ ->mDrawTarget->Snapshot();
+ static_cast<BasicAsyncReadbackBuffer*>(aDest)->TakeSurface(snapshot);
+ return true;
+}
+
+already_AddRefed<AsyncReadbackBuffer>
+BasicCompositor::CreateAsyncReadbackBuffer(const gfx::IntSize& aSize) {
+ return MakeAndAddRef<BasicAsyncReadbackBuffer>(aSize);
+}
+
+bool BasicCompositor::BlitRenderTarget(CompositingRenderTarget* aSource,
+ const gfx::IntSize& aSourceSize,
+ const gfx::IntSize& aDestSize) {
+ RefPtr<SourceSurface> surface =
+ static_cast<BasicCompositingRenderTarget*>(aSource)
+ ->mDrawTarget->Snapshot();
+ mRenderTarget->mDrawTarget->DrawSurface(
+ surface, Rect(Point(), Size(aDestSize)), Rect(Point(), Size(aSourceSize)),
+ DrawSurfaceOptions(), DrawOptions(1.0f, CompositionOp::OP_SOURCE));
+ return true;
+}
+
+Maybe<gfx::IntRect> BasicCompositor::BeginFrameForWindow(
+ const nsIntRegion& aInvalidRegion, const Maybe<IntRect>& aClipRect,
+ const IntRect& aRenderBounds, const nsIntRegion& aOpaqueRegion) {
+ if (mIsPendingEndRemoteDrawing) {
+ // Force to end previous remote drawing.
+ EndRemoteDrawing();
+ MOZ_ASSERT(!mIsPendingEndRemoteDrawing);
+ }
+
+ MOZ_RELEASE_ASSERT(mCurrentFrameDest == FrameDestination::NO_CURRENT_FRAME,
+ "mCurrentFrameDest not restored properly");
+
+ IntRect rect(IntPoint(), mWidget->GetClientSize().ToUnknownSize());
+
+ mShouldInvalidateWindow = NeedToRecreateFullWindowRenderTarget();
+
+ if (mShouldInvalidateWindow) {
+ mInvalidRegion = rect;
+ } else {
+ IntRegion invalidRegionSafe;
+ // Sometimes the invalid region is larger than we want to draw.
+ invalidRegionSafe.And(aInvalidRegion, rect);
+
+ mInvalidRegion = invalidRegionSafe;
+ }
+
+ LayoutDeviceIntRegion invalidRegion =
+ LayoutDeviceIntRegion::FromUnknownRegion(mInvalidRegion);
+ BufferMode bufferMode = BufferMode::BUFFERED;
+ // StartRemoteDrawingInRegion can mutate invalidRegion.
+ RefPtr<DrawTarget> dt =
+ mWidget->StartRemoteDrawingInRegion(invalidRegion, &bufferMode);
+ if (!dt) {
+ return Nothing();
+ }
+ if (invalidRegion.IsEmpty()) {
+ mWidget->EndRemoteDrawingInRegion(dt, invalidRegion);
+ return Nothing();
+ }
+
+ mInvalidRegion = invalidRegion.ToUnknownRegion();
+ IntRegion clearRegion;
+ clearRegion.Sub(mInvalidRegion, aOpaqueRegion);
+
+ RefPtr<CompositingRenderTarget> target;
+ if (bufferMode == BufferMode::BUFFERED) {
+ // Buffer drawing via a back buffer.
+ IntRect backBufferRect = mInvalidRegion.GetBounds();
+ bool isCleared = false;
+ RefPtr<DrawTarget> backBuffer =
+ mWidget->GetBackBufferDrawTarget(dt, backBufferRect, &isCleared);
+ if (!backBuffer) {
+ mWidget->EndRemoteDrawingInRegion(dt, invalidRegion);
+ return Nothing();
+ }
+ // Set up a render target for drawirg to the back buffer.
+ target = CreateRootRenderTarget(backBuffer, backBufferRect,
+ isCleared ? IntRegion() : clearRegion);
+ mFrontBuffer = dt;
+ // We will copy the drawing from the back buffer into mFrontBuffer (the
+ // widget) in EndRemoteDrawing().
+ } else {
+ // In BufferMode::BUFFER_NONE, the DrawTarget returned by
+ // StartRemoteDrawingInRegion can cover different rectangles in window
+ // space. It can either cover the entire window, or it can cover just the
+ // invalid region. We discern between the two cases by comparing the
+ // DrawTarget's size with the invalild region's size.
+ IntRect invalidRect = mInvalidRegion.GetBounds();
+ IntPoint dtLocation = dt->GetSize() == invalidRect.Size()
+ ? invalidRect.TopLeft()
+ : IntPoint(0, 0);
+ IntRect dtBounds(dtLocation, dt->GetSize());
+
+ // Set up a render target for drawing directly to dt.
+ target = CreateRootRenderTarget(dt, dtBounds, clearRegion);
+ }
+
+ mCurrentFrameDest = FrameDestination::WINDOW;
+
+ MOZ_RELEASE_ASSERT(target);
+ SetRenderTarget(target);
+
+ gfxUtils::ClipToRegion(mRenderTarget->mDrawTarget, mInvalidRegion);
+
+ mRenderTarget->mDrawTarget->PushClipRect(Rect(aClipRect.valueOr(rect)));
+
+ return Some(rect);
+}
+
+Maybe<gfx::IntRect> BasicCompositor::BeginFrameForTarget(
+ const nsIntRegion& aInvalidRegion, const Maybe<IntRect>& aClipRect,
+ const IntRect& aRenderBounds, const nsIntRegion& aOpaqueRegion,
+ DrawTarget* aTarget, const IntRect& aTargetBounds) {
+ if (mIsPendingEndRemoteDrawing) {
+ // Force to end previous remote drawing.
+ EndRemoteDrawing();
+ MOZ_ASSERT(!mIsPendingEndRemoteDrawing);
+ }
+
+ MOZ_RELEASE_ASSERT(mCurrentFrameDest == FrameDestination::NO_CURRENT_FRAME,
+ "mCurrentFrameDest not restored properly");
+
+ mInvalidRegion.And(aInvalidRegion, aTargetBounds);
+ MOZ_RELEASE_ASSERT(!mInvalidRegion.IsEmpty());
+
+ IntRegion clearRegion;
+ clearRegion.Sub(mInvalidRegion, aOpaqueRegion);
+
+ // Set up a render target for drawing directly to aTarget.
+ RefPtr<CompositingRenderTarget> target =
+ CreateRootRenderTarget(aTarget, aTargetBounds, clearRegion);
+ MOZ_RELEASE_ASSERT(target);
+ SetRenderTarget(target);
+
+ mCurrentFrameDest = FrameDestination::TARGET;
+
+ gfxUtils::ClipToRegion(mRenderTarget->mDrawTarget, mInvalidRegion);
+
+ mRenderTarget->mDrawTarget->PushClipRect(
+ Rect(aClipRect.valueOr(aTargetBounds)));
+
+ return Some(aTargetBounds);
+}
+
+void BasicCompositor::BeginFrameForNativeLayers() {
+ if (mIsPendingEndRemoteDrawing) {
+ // Force to end previous remote drawing.
+ EndRemoteDrawing();
+ MOZ_ASSERT(!mIsPendingEndRemoteDrawing);
+ }
+
+ MOZ_RELEASE_ASSERT(mCurrentFrameDest == FrameDestination::NO_CURRENT_FRAME,
+ "mCurrentFrameDest not restored properly");
+
+ mShouldInvalidateWindow = NeedToRecreateFullWindowRenderTarget();
+
+ // Make a 1x1 dummy render target so that GetCurrentRenderTarget() returns
+ // something non-null even outside of calls to
+ // Begin/EndRenderingToNativeLayer.
+ if (!mNativeLayersReferenceRT) {
+ RefPtr<DrawTarget> dt = Factory::CreateDrawTarget(
+ gfxVars::ContentBackend(), IntSize(1, 1), SurfaceFormat::B8G8R8A8);
+ mNativeLayersReferenceRT =
+ new BasicCompositingRenderTarget(dt, IntRect(0, 0, 1, 1), IntPoint());
+ }
+ SetRenderTarget(mNativeLayersReferenceRT);
+
+ mCurrentFrameDest = FrameDestination::NATIVE_LAYERS;
+}
+
+Maybe<gfx::IntRect> BasicCompositor::BeginRenderingToNativeLayer(
+ const nsIntRegion& aInvalidRegion, const Maybe<gfx::IntRect>& aClipRect,
+ const nsIntRegion& aOpaqueRegion, NativeLayer* aNativeLayer) {
+ IntRect rect = aNativeLayer->GetRect();
+
+ // We only support a single invalid rect per native layer. This is a
+ // limitation that's imposed by the AttemptVideo[ConvertAnd]Scale functions,
+ // which require knowing the combined clip in DrawGeometry and can only handle
+ // a single clip rect.
+ IntRect invalidRect;
+ if (mShouldInvalidateWindow) {
+ invalidRect = rect;
+ } else {
+ IntRegion invalidRegion;
+ invalidRegion.And(aInvalidRegion, rect);
+ if (invalidRegion.IsEmpty()) {
+ return Nothing();
+ }
+ invalidRect = invalidRegion.GetBounds();
+ }
+ mInvalidRegion = invalidRect;
+
+ RefPtr<CompositingRenderTarget> target;
+ aNativeLayer->SetSurfaceIsFlipped(false);
+ IntRegion invalidRelativeToLayer = invalidRect - rect.TopLeft();
+ RefPtr<DrawTarget> dt = aNativeLayer->NextSurfaceAsDrawTarget(
+ gfx::IntRect({}, aNativeLayer->GetSize()), invalidRelativeToLayer,
+ BackendType::SKIA);
+ if (!dt) {
+ return Nothing();
+ }
+ mCurrentNativeLayer = aNativeLayer;
+ IntRegion clearRegion;
+ clearRegion.Sub(mInvalidRegion, aOpaqueRegion);
+ // Set up a render target for drawing directly to dt.
+ target = CreateRootRenderTarget(dt, rect, clearRegion);
+
+ MOZ_RELEASE_ASSERT(target);
+ SetRenderTarget(target);
+
+ IntRect clipRect = invalidRect;
+ if (aClipRect) {
+ clipRect = clipRect.Intersect(*aClipRect);
+ }
+ mRenderTarget->SetClipRect(Some(clipRect));
+
+ return Some(rect);
+}
+
+void BasicCompositor::EndRenderingToNativeLayer() {
+ mRenderTarget->SetClipRect(Nothing());
+ SetRenderTarget(mNativeLayersReferenceRT);
+
+ MOZ_RELEASE_ASSERT(mCurrentNativeLayer);
+ mCurrentNativeLayer->NotifySurfaceReady();
+ mCurrentNativeLayer = nullptr;
+}
+
+void BasicCompositor::EndFrame() {
+ Compositor::EndFrame();
+
+ if (mCurrentFrameDest != FrameDestination::NATIVE_LAYERS) {
+ // Pop aClipRect/bounds rect
+ mRenderTarget->mDrawTarget->PopClip();
+
+ if (StaticPrefs::nglayout_debug_widget_update_flashing()) {
+ float r = float(rand()) / float(RAND_MAX);
+ float g = float(rand()) / float(RAND_MAX);
+ float b = float(rand()) / float(RAND_MAX);
+ // We're still clipped to mInvalidRegion, so just fill the bounds.
+ mRenderTarget->mDrawTarget->FillRect(
+ Rect(mInvalidRegion.GetBounds()),
+ ColorPattern(DeviceColor(r, g, b, 0.2f)));
+ }
+
+ // Pop aInvalidRegion
+ mRenderTarget->mDrawTarget->PopClip();
+ }
+
+ // Reset the translation that was applied in CreateRootRenderTarget.
+ mRenderTarget->mDrawTarget->SetTransform(gfx::Matrix());
+
+ switch (mCurrentFrameDest) {
+ case FrameDestination::NO_CURRENT_FRAME:
+ MOZ_CRASH("EndFrame being called without BeginFrameForXYZ?");
+ break;
+ case FrameDestination::WINDOW:
+ TryToEndRemoteDrawing();
+ break;
+ case FrameDestination::TARGET:
+ case FrameDestination::NATIVE_LAYERS:
+ mRenderTarget = nullptr;
+ break;
+ }
+ mCurrentFrameDest = FrameDestination::NO_CURRENT_FRAME;
+ mShouldInvalidateWindow = false;
+}
+
+RefPtr<SurfacePoolHandle> BasicCompositor::GetSurfacePoolHandle() {
+#ifdef XP_MACOSX
+ if (!mSurfacePoolHandle) {
+ mSurfacePoolHandle = SurfacePool::Create(0)->GetHandleForGL(nullptr);
+ }
+#endif
+ return mSurfacePoolHandle;
+}
+
+void BasicCompositor::TryToEndRemoteDrawing() {
+ if (mIsDestroyed || !mRenderTarget) {
+ return;
+ }
+
+ // If it is not a good time to call EndRemoteDrawing, defer it.
+ if (NeedsToDeferEndRemoteDrawing()) {
+ mIsPendingEndRemoteDrawing = true;
+
+ const uint32_t retryMs = 2;
+ RefPtr<BasicCompositor> self = this;
+ RefPtr<Runnable> runnable =
+ NS_NewRunnableFunction("layers::BasicCompositor::TryToEndRemoteDrawing",
+ [self]() { self->TryToEndRemoteDrawing(); });
+ GetCurrentSerialEventTarget()->DelayedDispatch(runnable.forget(), retryMs);
+ } else {
+ EndRemoteDrawing();
+ }
+}
+
+void BasicCompositor::EndRemoteDrawing() {
+ if (mIsDestroyed || !mRenderTarget) {
+ return;
+ }
+
+ if (mFrontBuffer) {
+ // This is the case where we have a back buffer for BufferMode::BUFFERED
+ // drawing.
+ // mRenderTarget->mDrawTarget is the back buffer.
+ // mFrontBuffer is always located at (0, 0) in window space.
+ RefPtr<SourceSurface> source = mWidget->EndBackBufferDrawing();
+ IntPoint srcOffset = mRenderTarget->GetOrigin();
+
+ // The source DrawTarget is clipped to the invalidation region, so we have
+ // to copy the individual rectangles in the region or else we'll draw
+ // garbage pixels.
+ // CopySurface ignores both the transform and the clip.
+ for (auto iter = mInvalidRegion.RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& r = iter.Get();
+ mFrontBuffer->CopySurface(source, r - srcOffset, r.TopLeft());
+ }
+
+ mWidget->EndRemoteDrawingInRegion(
+ mFrontBuffer, LayoutDeviceIntRegion::FromUnknownRegion(mInvalidRegion));
+
+ mFrontBuffer = nullptr;
+ } else {
+ mWidget->EndRemoteDrawingInRegion(
+ mRenderTarget->mDrawTarget,
+ LayoutDeviceIntRegion::FromUnknownRegion(mInvalidRegion));
+ }
+
+ mRenderTarget = nullptr;
+ mIsPendingEndRemoteDrawing = false;
+}
+
+void BasicCompositor::NormalDrawingDone() {
+ // Now is a good time to update mFullWindowRenderTarget.
+
+ if (!ShouldRecordFrames()) {
+ // If we are no longer recording a profile, we can drop the render target if
+ // it exists.
+ mFullWindowRenderTarget = nullptr;
+ return;
+ }
+
+ if (NeedToRecreateFullWindowRenderTarget()) {
+ // We have either (1) just started recording and not yet allocated a
+ // buffer or (2) are already recording and have resized the window. In
+ // either case, we need a new render target.
+ IntRect windowRect(IntPoint(0, 0),
+ mWidget->GetClientSize().ToUnknownSize());
+ RefPtr<gfx::DrawTarget> drawTarget =
+ mRenderTarget->mDrawTarget->CreateSimilarDrawTarget(
+ windowRect.Size(), mRenderTarget->mDrawTarget->GetFormat());
+
+ mFullWindowRenderTarget =
+ new BasicCompositingRenderTarget(drawTarget, windowRect, IntPoint());
+ }
+
+ RefPtr<SourceSurface> source = mRenderTarget->mDrawTarget->Snapshot();
+ IntPoint srcOffset = mRenderTarget->GetOrigin();
+ for (auto iter = mInvalidRegion.RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& r = iter.Get();
+ mFullWindowRenderTarget->mDrawTarget->CopySurface(source, r - srcOffset,
+ r.TopLeft());
+ }
+ mFullWindowRenderTarget->mDrawTarget->Flush();
+}
+
+bool BasicCompositor::NeedsToDeferEndRemoteDrawing() {
+ return mFrontBuffer && mWidget->NeedsToDeferEndRemoteDrawing();
+}
+
+void BasicCompositor::FinishPendingComposite() { EndRemoteDrawing(); }
+
+bool BasicCompositor::NeedToRecreateFullWindowRenderTarget() const {
+ if (!ShouldRecordFrames()) {
+ return false;
+ }
+ if (!mFullWindowRenderTarget) {
+ return true;
+ }
+ IntSize windowSize = mWidget->GetClientSize().ToUnknownSize();
+ return mFullWindowRenderTarget->mDrawTarget->GetSize() != windowSize;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/basic/BasicCompositor.h b/gfx/layers/basic/BasicCompositor.h
new file mode 100644
index 0000000000..7044088e0e
--- /dev/null
+++ b/gfx/layers/basic/BasicCompositor.h
@@ -0,0 +1,240 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef MOZILLA_GFX_BASICCOMPOSITOR_H
+#define MOZILLA_GFX_BASICCOMPOSITOR_H
+
+#include "mozilla/layers/Compositor.h"
+#include "mozilla/layers/TextureHost.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Triangle.h"
+#include "mozilla/gfx/Polygon.h"
+
+namespace mozilla {
+namespace layers {
+
+class BasicCompositingRenderTarget : public CompositingRenderTarget {
+ public:
+ BasicCompositingRenderTarget(gfx::DrawTarget* aDrawTarget,
+ const gfx::IntRect& aRect,
+ const gfx::IntPoint& aClipSpaceOrigin)
+ : CompositingRenderTarget(aRect.TopLeft()),
+ mDrawTarget(aDrawTarget),
+ mSize(aRect.Size()),
+ mClipSpaceOrigin(aClipSpaceOrigin) {}
+
+ const char* Name() const override { return "BasicCompositingRenderTarget"; }
+
+ gfx::IntSize GetSize() const override { return mSize; }
+
+ // The point that DrawGeometry's aClipRect is relative to. Will be (0, 0) for
+ // root render targets and equal to GetOrigin() for non-root render targets.
+ gfx::IntPoint GetClipSpaceOrigin() const { return mClipSpaceOrigin; }
+
+ // In render target coordinates, i.e. the same space as GetOrigin().
+ // NOT relative to mClipSpaceOrigin!
+ void SetClipRect(const Maybe<gfx::IntRect>& aRect) { mClipRect = aRect; }
+ const Maybe<gfx::IntRect>& GetClipRect() const { return mClipRect; }
+
+ void BindRenderTarget();
+
+ gfx::SurfaceFormat GetFormat() const override {
+ return mDrawTarget ? mDrawTarget->GetFormat()
+ : gfx::SurfaceFormat(gfx::SurfaceFormat::UNKNOWN);
+ }
+
+ RefPtr<gfx::DrawTarget> mDrawTarget;
+ gfx::IntSize mSize;
+ gfx::IntPoint mClipSpaceOrigin;
+ Maybe<gfx::IntRect> mClipRect;
+};
+
+class BasicCompositor : public Compositor {
+ public:
+ BasicCompositor(CompositorBridgeParent* aParent,
+ widget::CompositorWidget* aWidget);
+
+ protected:
+ virtual ~BasicCompositor();
+
+ public:
+ BasicCompositor* AsBasicCompositor() override { return this; }
+
+ bool Initialize(nsCString* const out_failureReason) override;
+
+ void Destroy() override;
+
+ TextureFactoryIdentifier GetTextureFactoryIdentifier() override;
+
+ already_AddRefed<CompositingRenderTarget> CreateRenderTarget(
+ const gfx::IntRect& aRect, SurfaceInitMode aInit) override;
+
+ already_AddRefed<CompositingRenderTarget> CreateRenderTargetFromSource(
+ const gfx::IntRect& aRect, const CompositingRenderTarget* aSource,
+ const gfx::IntPoint& aSourcePoint) override;
+
+ virtual already_AddRefed<CompositingRenderTarget> CreateRootRenderTarget(
+ gfx::DrawTarget* aDrawTarget, const gfx::IntRect& aDrawTargetRect,
+ const gfx::IntRegion& aClearRegion);
+
+ already_AddRefed<DataTextureSource> CreateDataTextureSource(
+ TextureFlags aFlags = TextureFlags::NO_FLAGS) override;
+
+ already_AddRefed<DataTextureSource> CreateDataTextureSourceAround(
+ gfx::DataSourceSurface* aSurface) override;
+
+ already_AddRefed<DataTextureSource> CreateDataTextureSourceAroundYCbCr(
+ TextureHost* aTexture) override;
+
+ bool SupportsEffect(EffectTypes aEffect) override;
+
+ bool SupportsLayerGeometry() const override;
+
+ bool ReadbackRenderTarget(CompositingRenderTarget* aSource,
+ AsyncReadbackBuffer* aDest) override;
+
+ already_AddRefed<AsyncReadbackBuffer> CreateAsyncReadbackBuffer(
+ const gfx::IntSize& aSize) override;
+
+ bool BlitRenderTarget(CompositingRenderTarget* aSource,
+ const gfx::IntSize& aSourceSize,
+ const gfx::IntSize& aDestSize) override;
+
+ void SetRenderTarget(CompositingRenderTarget* aSource) override {
+ mRenderTarget = static_cast<BasicCompositingRenderTarget*>(aSource);
+ mRenderTarget->BindRenderTarget();
+ }
+
+ already_AddRefed<CompositingRenderTarget> GetWindowRenderTarget()
+ const override {
+ return do_AddRef(mFullWindowRenderTarget);
+ }
+
+ already_AddRefed<CompositingRenderTarget> GetCurrentRenderTarget()
+ const override {
+ return do_AddRef(mRenderTarget);
+ }
+
+ void DrawQuad(const gfx::Rect& aRect, const gfx::IntRect& aClipRect,
+ const EffectChain& aEffectChain, gfx::Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect) override;
+
+ void ClearRect(const gfx::Rect& aRect) override;
+
+ Maybe<gfx::IntRect> BeginFrameForWindow(
+ const nsIntRegion& aInvalidRegion, const Maybe<gfx::IntRect>& aClipRect,
+ const gfx::IntRect& aRenderBounds,
+ const nsIntRegion& aOpaqueRegion) override;
+
+ Maybe<gfx::IntRect> BeginFrameForTarget(
+ const nsIntRegion& aInvalidRegion, const Maybe<gfx::IntRect>& aClipRect,
+ const gfx::IntRect& aRenderBounds, const nsIntRegion& aOpaqueRegion,
+ gfx::DrawTarget* aTarget, const gfx::IntRect& aTargetBounds) override;
+
+ void BeginFrameForNativeLayers() override;
+
+ Maybe<gfx::IntRect> BeginRenderingToNativeLayer(
+ const nsIntRegion& aInvalidRegion, const Maybe<gfx::IntRect>& aClipRect,
+ const nsIntRegion& aOpaqueRegion, NativeLayer* aNativeLayer) override;
+
+ void EndRenderingToNativeLayer() override;
+
+ void NormalDrawingDone() override;
+ void EndFrame() override;
+
+ RefPtr<SurfacePoolHandle> GetSurfacePoolHandle() override;
+
+ bool SupportsPartialTextureUpdate() override { return true; }
+ bool CanUseCanvasLayerForSize(const gfx::IntSize& aSize) override {
+ return true;
+ }
+ int32_t GetMaxTextureSize() const override;
+ void SetDestinationSurfaceSize(const gfx::IntSize& aSize) override {}
+
+ void MakeCurrent(MakeCurrentFlags aFlags = 0) override {}
+
+#ifdef MOZ_DUMP_PAINTING
+ const char* Name() const override { return "Basic"; }
+#endif // MOZ_DUMP_PAINTING
+
+ LayersBackend GetBackendType() const override {
+ return LayersBackend::LAYERS_BASIC;
+ }
+
+ bool IsPendingComposite() override { return mIsPendingEndRemoteDrawing; }
+
+ void FinishPendingComposite() override;
+
+ private:
+ template <typename Geometry>
+ void DrawGeometry(const Geometry& aGeometry, const gfx::Rect& aRect,
+ const gfx::IntRect& aClipRect,
+ const EffectChain& aEffectChain, gfx::Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect, const bool aEnableAA);
+
+ void DrawPolygon(const gfx::Polygon& aPolygon, const gfx::Rect& aRect,
+ const gfx::IntRect& aClipRect,
+ const EffectChain& aEffectChain, gfx::Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect) override;
+
+ void TryToEndRemoteDrawing();
+ void EndRemoteDrawing();
+
+ bool NeedsToDeferEndRemoteDrawing();
+
+ bool NeedToRecreateFullWindowRenderTarget() const;
+
+ // When rendering to a back buffer, this is the front buffer that the contents
+ // of the back buffer need to be copied to. Only non-null between
+ // BeginFrameForWindow and EndRemoteDrawing, and only when using a back
+ // buffer.
+ RefPtr<gfx::DrawTarget> mFrontBuffer;
+
+ // The current render target for drawing
+ RefPtr<BasicCompositingRenderTarget> mRenderTarget;
+
+ // The native layer that we're currently rendering to, if any.
+ // Non-null only between BeginFrameForWindow and EndFrame if
+ // BeginFrameForWindow has been called with a non-null aNativeLayer.
+ RefPtr<NativeLayer> mCurrentNativeLayer;
+
+ RefPtr<SurfacePoolHandle> mSurfacePoolHandle;
+
+ gfx::IntRegion mInvalidRegion;
+
+ uint32_t mMaxTextureSize;
+ bool mIsPendingEndRemoteDrawing;
+ bool mShouldInvalidateWindow = false;
+
+ // Where the current frame is being rendered to.
+ enum class FrameDestination : uint8_t {
+ NO_CURRENT_FRAME, // before BeginFrameForXYZ or after EndFrame
+ WINDOW, // between BeginFrameForWindow and EndFrame
+ TARGET, // between BeginFrameForTarget and EndFrame
+ NATIVE_LAYERS // between BeginFrameForNativeLayers and EndFrame
+ };
+ FrameDestination mCurrentFrameDest = FrameDestination::NO_CURRENT_FRAME;
+
+ // mDrawTarget will not be the full window on all platforms. We therefore need
+ // to keep a full window render target around when we are capturing
+ // screenshots on those platforms.
+ RefPtr<BasicCompositingRenderTarget> mFullWindowRenderTarget;
+
+ // The 1x1 dummy render target that's the "current" render target between
+ // BeginFrameForNativeLayers and EndFrame but outside pairs of
+ // Begin/EndRenderingToNativeLayer. Created on demand.
+ RefPtr<CompositingRenderTarget> mNativeLayersReferenceRT;
+};
+
+BasicCompositor* AssertBasicCompositor(Compositor* aCompositor);
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_BASICCOMPOSITOR_H */
diff --git a/gfx/layers/basic/BasicContainerLayer.cpp b/gfx/layers/basic/BasicContainerLayer.cpp
new file mode 100644
index 0000000000..7cb4697d9e
--- /dev/null
+++ b/gfx/layers/basic/BasicContainerLayer.cpp
@@ -0,0 +1,162 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "BasicContainerLayer.h"
+#include <sys/types.h> // for int32_t
+#include "BasicLayersImpl.h" // for ToData
+#include "basic/BasicImplData.h" // for BasicImplData
+#include "basic/BasicLayers.h" // for BasicLayerManager
+#include "mozilla/gfx/BaseRect.h" // for BaseRect
+#include "mozilla/mozalloc.h" // for operator new
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsISupportsImpl.h" // for Layer::AddRef, etc
+#include "nsPoint.h" // for nsIntPoint
+#include "nsRegion.h" // for nsIntRegion
+#include "ReadbackProcessor.h"
+
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace layers {
+
+BasicContainerLayer::~BasicContainerLayer() {
+ ContainerLayer::RemoveAllChildren();
+ MOZ_COUNT_DTOR(BasicContainerLayer);
+}
+
+void BasicContainerLayer::ComputeEffectiveTransforms(
+ const Matrix4x4& aTransformToSurface) {
+ // We push groups for container layers if we need to, which always
+ // are aligned in device space, so it doesn't really matter how we snap
+ // containers.
+ Matrix residual;
+ Matrix4x4 transformToSurface = aTransformToSurface;
+ bool participate3DCtx = Extend3DContext() || Is3DContextLeaf();
+ if (!participate3DCtx && GetContentFlags() & CONTENT_BACKFACE_HIDDEN) {
+ // For backface-hidden layers
+ transformToSurface.ProjectTo2D();
+ }
+ Matrix4x4 idealTransform = GetLocalTransform() * transformToSurface;
+ if (!participate3DCtx && !(GetContentFlags() & CONTENT_BACKFACE_HIDDEN)) {
+ // For non-backface-hidden layers,
+ // 3D components are required to handle CONTENT_BACKFACE_HIDDEN.
+ idealTransform.ProjectTo2D();
+ }
+
+ if (!idealTransform.CanDraw2D()) {
+ if (!Extend3DContext()) {
+ mEffectiveTransform = idealTransform;
+ ComputeEffectiveTransformsForChildren(Matrix4x4());
+ ComputeEffectiveTransformForMaskLayers(Matrix4x4());
+ mUseIntermediateSurface = true;
+ return;
+ }
+
+ mEffectiveTransform = idealTransform;
+ ComputeEffectiveTransformsForChildren(idealTransform);
+ ComputeEffectiveTransformForMaskLayers(idealTransform);
+ mUseIntermediateSurface = false;
+ return;
+ }
+
+ // With 2D transform or extended 3D context.
+
+ Layer* child = GetFirstChild();
+ bool hasSingleBlendingChild = false;
+ if (!HasMultipleChildren() && child) {
+ hasSingleBlendingChild = child->GetMixBlendMode() != CompositionOp::OP_OVER;
+ }
+
+ /* If we have a single childand it is not blending,, it can just inherit our
+ * opacity, otherwise we need a PushGroup and we need to mark ourselves as
+ * using an intermediate surface so our children don't inherit our opacity via
+ * GetEffectiveOpacity. Having a mask layer always forces our own push group
+ * Having a blend mode also always forces our own push group
+ */
+ mUseIntermediateSurface =
+ GetMaskLayer() || GetForceIsolatedGroup() ||
+ (GetMixBlendMode() != CompositionOp::OP_OVER && HasMultipleChildren()) ||
+ (GetEffectiveOpacity() != 1.0 &&
+ ((HasMultipleChildren() && !Extend3DContext()) ||
+ hasSingleBlendingChild));
+
+ mEffectiveTransform =
+ !mUseIntermediateSurface
+ ? idealTransform
+ : (!(GetContentFlags() & CONTENT_BACKFACE_HIDDEN)
+ ? SnapTransformTranslation(idealTransform, &residual)
+ : SnapTransformTranslation3D(idealTransform, &residual));
+ Matrix4x4 childTransformToSurface =
+ (!mUseIntermediateSurface ||
+ (mUseIntermediateSurface && !Extend3DContext() /* 2D */))
+ ? idealTransform
+ : Matrix4x4::From2D(residual);
+ ComputeEffectiveTransformsForChildren(childTransformToSurface);
+
+ ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
+}
+
+bool BasicContainerLayer::ChildrenPartitionVisibleRegion(
+ const gfx::IntRect& aInRect) {
+ Matrix transform;
+ if (!GetEffectiveTransform().CanDraw2D(&transform) ||
+ ThebesMatrix(transform).HasNonIntegerTranslation())
+ return false;
+
+ nsIntPoint offset(int32_t(transform._31), int32_t(transform._32));
+ gfx::IntRect rect = aInRect.Intersect(
+ GetLocalVisibleRegion().GetBounds().ToUnknownRect() + offset);
+ nsIntRegion covered;
+
+ for (Layer* l = mFirstChild; l; l = l->GetNextSibling()) {
+ if (ToData(l)->IsHidden()) continue;
+
+ Matrix childTransform;
+ if (!l->GetEffectiveTransform().CanDraw2D(&childTransform) ||
+ ThebesMatrix(childTransform).HasNonIntegerTranslation() ||
+ l->GetEffectiveOpacity() != 1.0)
+ return false;
+ nsIntRegion childRegion = l->GetLocalVisibleRegion().ToUnknownRegion();
+ childRegion.MoveBy(int32_t(childTransform._31),
+ int32_t(childTransform._32));
+ childRegion.And(childRegion, rect);
+ if (l->GetClipRect()) {
+ childRegion.And(childRegion, l->GetClipRect()->ToUnknownRect() + offset);
+ }
+ nsIntRegion intersection;
+ intersection.And(covered, childRegion);
+ if (!intersection.IsEmpty()) return false;
+ covered.Or(covered, childRegion);
+ }
+
+ return covered.Contains(rect);
+}
+
+void BasicContainerLayer::Validate(
+ LayerManager::DrawPaintedLayerCallback aCallback, void* aCallbackData,
+ ReadbackProcessor* aReadback) {
+ ReadbackProcessor readback;
+ if (BasicManager()->IsRetained()) {
+ readback.BuildUpdates(this);
+ }
+ for (Layer* l = mFirstChild; l; l = l->GetNextSibling()) {
+ BasicImplData* data = ToData(l);
+ data->Validate(aCallback, aCallbackData, &readback);
+ if (l->GetMaskLayer()) {
+ data = ToData(l->GetMaskLayer());
+ data->Validate(aCallback, aCallbackData, nullptr);
+ }
+ }
+}
+
+already_AddRefed<ContainerLayer> BasicLayerManager::CreateContainerLayer() {
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ RefPtr<ContainerLayer> layer = new BasicContainerLayer(this);
+ return layer.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/basic/BasicContainerLayer.h b/gfx/layers/basic/BasicContainerLayer.h
new file mode 100644
index 0000000000..07a3183a54
--- /dev/null
+++ b/gfx/layers/basic/BasicContainerLayer.h
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef GFX_BASICCONTAINERLAYER_H
+#define GFX_BASICCONTAINERLAYER_H
+
+#include "BasicImplData.h" // for BasicImplData
+#include "BasicLayers.h" // for BasicLayerManager
+#include "Layers.h" // for Layer, ContainerLayer
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR
+#include "nsISupportsUtils.h" // for NS_ADDREF, NS_RELEASE
+#include "mozilla/gfx/Rect.h"
+
+namespace mozilla {
+namespace layers {
+
+class BasicContainerLayer : public ContainerLayer, public BasicImplData {
+ public:
+ explicit BasicContainerLayer(BasicLayerManager* aManager)
+ : ContainerLayer(aManager, static_cast<BasicImplData*>(this)) {
+ MOZ_COUNT_CTOR(BasicContainerLayer);
+ mSupportsComponentAlphaChildren = true;
+ }
+
+ protected:
+ virtual ~BasicContainerLayer();
+
+ public:
+ void SetVisibleRegion(const LayerIntRegion& aRegion) override {
+ NS_ASSERTION(BasicManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ ContainerLayer::SetVisibleRegion(aRegion);
+ }
+ bool InsertAfter(Layer* aChild, Layer* aAfter) override {
+ if (!BasicManager()->InConstruction()) {
+ NS_ERROR("Can only set properties in construction phase");
+ return false;
+ }
+ return ContainerLayer::InsertAfter(aChild, aAfter);
+ }
+
+ bool RemoveChild(Layer* aChild) override {
+ if (!BasicManager()->InConstruction()) {
+ NS_ERROR("Can only set properties in construction phase");
+ return false;
+ }
+ return ContainerLayer::RemoveChild(aChild);
+ }
+
+ bool RepositionChild(Layer* aChild, Layer* aAfter) override {
+ if (!BasicManager()->InConstruction()) {
+ NS_ERROR("Can only set properties in construction phase");
+ return false;
+ }
+ return ContainerLayer::RepositionChild(aChild, aAfter);
+ }
+
+ void ComputeEffectiveTransforms(
+ const gfx::Matrix4x4& aTransformToSurface) override;
+
+ /**
+ * Returns true when:
+ * a) no (non-hidden) childrens' visible areas overlap in
+ * (aInRect intersected with this layer's visible region).
+ * b) the (non-hidden) childrens' visible areas cover
+ * (aInRect intersected with this layer's visible region).
+ * c) this layer and all (non-hidden) children have transforms that are
+ * translations by integers. aInRect is in the root coordinate system. Child
+ * layers with opacity do not contribute to the covered area in check b). This
+ * method can be conservative; it's OK to return false under any
+ * circumstances.
+ */
+ bool ChildrenPartitionVisibleRegion(const gfx::IntRect& aInRect);
+
+ void ForceIntermediateSurface() { mUseIntermediateSurface = true; }
+
+ void SetSupportsComponentAlphaChildren(bool aSupports) {
+ mSupportsComponentAlphaChildren = aSupports;
+ }
+
+ void Validate(LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData, ReadbackProcessor* aReadback) override;
+
+ /**
+ * We don't really have a hard restriction for max layer size, but we pick
+ * 4096 to avoid excessive memory usage.
+ */
+ int32_t GetMaxLayerSize() override { return 4096; }
+
+ protected:
+ BasicLayerManager* BasicManager() {
+ return static_cast<BasicLayerManager*>(mManager);
+ }
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/basic/BasicImageLayer.cpp b/gfx/layers/basic/BasicImageLayer.cpp
new file mode 100644
index 0000000000..d1b392154f
--- /dev/null
+++ b/gfx/layers/basic/BasicImageLayer.cpp
@@ -0,0 +1,108 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "BasicLayersImpl.h" // for FillRectWithMask, etc
+#include "ImageContainer.h" // for AutoLockImage, etc
+#include "ImageLayers.h" // for ImageLayer
+#include "Layers.h" // for Layer (ptr only), etc
+#include "basic/BasicImplData.h" // for BasicImplData
+#include "basic/BasicLayers.h" // for BasicLayerManager
+#include "mozilla/mozalloc.h" // for operator new
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for gfxPattern::Release, etc
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsRegion.h" // for nsIntRegion
+#include "mozilla/gfx/Point.h" // for IntSize
+
+using namespace mozilla::gfx;
+
+namespace mozilla::layers {
+
+class BasicImageLayer : public ImageLayer, public BasicImplData {
+ public:
+ explicit BasicImageLayer(BasicLayerManager* aLayerManager)
+ : ImageLayer(aLayerManager, static_cast<BasicImplData*>(this)),
+ mSize(-1, -1) {
+ MOZ_COUNT_CTOR(BasicImageLayer);
+ }
+
+ protected:
+ MOZ_COUNTED_DTOR_OVERRIDE(BasicImageLayer)
+
+ public:
+ void SetVisibleRegion(const LayerIntRegion& aRegion) override {
+ NS_ASSERTION(BasicManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ ImageLayer::SetVisibleRegion(aRegion);
+ }
+
+ void Paint(DrawTarget* aDT, const gfx::Point& aDeviceOffset,
+ Layer* aMaskLayer) override;
+
+ already_AddRefed<SourceSurface> GetAsSourceSurface() override;
+
+ protected:
+ BasicLayerManager* BasicManager() {
+ return static_cast<BasicLayerManager*>(mManager);
+ }
+
+ gfx::IntSize mSize;
+};
+
+void BasicImageLayer::Paint(DrawTarget* aDT, const gfx::Point& aDeviceOffset,
+ Layer* aMaskLayer) {
+ if (IsHidden() || !mContainer) {
+ return;
+ }
+
+ RefPtr<ImageFactory> originalIF = mContainer->GetImageFactory();
+ mContainer->SetImageFactory(mManager->IsCompositingCheap()
+ ? nullptr
+ : BasicManager()->GetImageFactory());
+
+ AutoLockImage autoLock(mContainer);
+ Image* image = autoLock.GetImage(BasicManager()->GetCompositionTime());
+ if (!image) {
+ mContainer->SetImageFactory(originalIF);
+ return;
+ }
+ RefPtr<gfx::SourceSurface> surface = image->GetAsSourceSurface();
+ if (!surface || !surface->IsValid()) {
+ mContainer->SetImageFactory(originalIF);
+ return;
+ }
+
+ gfx::IntSize size = mSize = surface->GetSize();
+ FillRectWithMask(
+ aDT, aDeviceOffset, Rect(0, 0, size.width, size.height), surface,
+ mSamplingFilter,
+ DrawOptions(GetEffectiveOpacity(), GetEffectiveOperator(this)),
+ aMaskLayer);
+
+ mContainer->SetImageFactory(originalIF);
+}
+
+already_AddRefed<SourceSurface> BasicImageLayer::GetAsSourceSurface() {
+ if (!mContainer) {
+ return nullptr;
+ }
+
+ AutoLockImage lockImage(mContainer);
+ Image* image = lockImage.GetImage();
+ if (!image) {
+ return nullptr;
+ }
+ return image->GetAsSourceSurface();
+}
+
+already_AddRefed<ImageLayer> BasicLayerManager::CreateImageLayer() {
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ RefPtr<ImageLayer> layer = new BasicImageLayer(this);
+ return layer.forget();
+}
+
+} // namespace mozilla::layers
diff --git a/gfx/layers/basic/BasicImages.cpp b/gfx/layers/basic/BasicImages.cpp
new file mode 100644
index 0000000000..7774e39f54
--- /dev/null
+++ b/gfx/layers/basic/BasicImages.cpp
@@ -0,0 +1,180 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 <stdint.h> // for uint8_t, uint32_t
+#include "BasicLayers.h" // for BasicLayerManager
+#include "ImageContainer.h" // for PlanarYCbCrImage, etc
+#include "ImageTypes.h" // for ImageFormat, etc
+#include "cairo.h" // for cairo_user_data_key_t
+#include "gfxASurface.h" // for gfxASurface, etc
+#include "gfxPlatform.h" // for gfxPlatform, gfxImageFormat
+#include "gfxUtils.h" // for gfxUtils
+#include "mozilla/CheckedInt.h"
+#include "mozilla/mozalloc.h" // for operator delete[], etc
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ERROR, NS_ASSERTION
+#include "nsISupportsImpl.h" // for Image::Release, etc
+#include "nsThreadUtils.h" // for NS_IsMainThread
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/DataSurfaceHelpers.h"
+#include "gfx2DGlue.h"
+#include "YCbCrUtils.h" // for YCbCr conversions
+
+namespace mozilla {
+namespace layers {
+
+class BasicPlanarYCbCrImage : public RecyclingPlanarYCbCrImage {
+ public:
+ BasicPlanarYCbCrImage(const gfx::IntSize& aScaleHint,
+ gfxImageFormat aOffscreenFormat,
+ BufferRecycleBin* aRecycleBin)
+ : RecyclingPlanarYCbCrImage(aRecycleBin),
+ mScaleHint(aScaleHint),
+ mStride(0),
+ mDelayedConversion(false) {
+ SetOffscreenFormat(aOffscreenFormat);
+ }
+
+ ~BasicPlanarYCbCrImage() {
+ if (mDecodedBuffer) {
+ // Right now this only happens if the Image was never drawn, otherwise
+ // this will have been tossed away at surface destruction.
+ mRecycleBin->RecycleBuffer(std::move(mDecodedBuffer),
+ mSize.height * mStride);
+ }
+ }
+
+ virtual bool CopyData(const Data& aData) override;
+ virtual void SetDelayedConversion(bool aDelayed) override {
+ mDelayedConversion = aDelayed;
+ }
+
+ already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override;
+
+ virtual size_t SizeOfIncludingThis(
+ MallocSizeOf aMallocSizeOf) const override {
+ return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+ }
+
+ virtual size_t SizeOfExcludingThis(
+ MallocSizeOf aMallocSizeOf) const override {
+ size_t size = RecyclingPlanarYCbCrImage::SizeOfExcludingThis(aMallocSizeOf);
+ size += aMallocSizeOf(mDecodedBuffer.get());
+ return size;
+ }
+
+ private:
+ UniquePtr<uint8_t[]> mDecodedBuffer;
+ gfx::IntSize mScaleHint;
+ int mStride;
+ bool mDelayedConversion;
+};
+
+class BasicImageFactory : public ImageFactory {
+ public:
+ BasicImageFactory() = default;
+
+ virtual RefPtr<PlanarYCbCrImage> CreatePlanarYCbCrImage(
+ const gfx::IntSize& aScaleHint, BufferRecycleBin* aRecycleBin) override {
+ return new BasicPlanarYCbCrImage(
+ aScaleHint, gfxPlatform::GetPlatform()->GetOffscreenFormat(),
+ aRecycleBin);
+ }
+};
+
+bool BasicPlanarYCbCrImage::CopyData(const Data& aData) {
+ RecyclingPlanarYCbCrImage::CopyData(aData);
+
+ if (mDelayedConversion) {
+ return false;
+ }
+
+ // Do some sanity checks to prevent integer overflow
+ if (aData.mYSize.width > PlanarYCbCrImage::MAX_DIMENSION ||
+ aData.mYSize.height > PlanarYCbCrImage::MAX_DIMENSION) {
+ NS_ERROR("Illegal image source width or height");
+ return false;
+ }
+
+ gfx::SurfaceFormat format =
+ gfx::ImageFormatToSurfaceFormat(GetOffscreenFormat());
+
+ gfx::IntSize size(mScaleHint);
+ gfx::GetYCbCrToRGBDestFormatAndSize(aData, format, size);
+ if (size.width > PlanarYCbCrImage::MAX_DIMENSION ||
+ size.height > PlanarYCbCrImage::MAX_DIMENSION) {
+ NS_ERROR("Illegal image dest width or height");
+ return false;
+ }
+
+ mStride = gfx::StrideForFormatAndWidth(format, size.width);
+ mozilla::CheckedInt32 requiredBytes =
+ mozilla::CheckedInt32(size.height) * mozilla::CheckedInt32(mStride);
+ if (!requiredBytes.isValid()) {
+ // invalid size
+ return false;
+ }
+ mDecodedBuffer = AllocateBuffer(requiredBytes.value());
+ if (!mDecodedBuffer) {
+ // out of memory
+ return false;
+ }
+
+ gfx::ConvertYCbCrToRGB(aData, format, size, mDecodedBuffer.get(), mStride);
+ SetOffscreenFormat(gfx::SurfaceFormatToImageFormat(format));
+ mSize = size;
+
+ return true;
+}
+
+already_AddRefed<gfx::SourceSurface>
+BasicPlanarYCbCrImage::GetAsSourceSurface() {
+ NS_ASSERTION(NS_IsMainThread(), "Must be main thread");
+
+ if (mSourceSurface) {
+ RefPtr<gfx::SourceSurface> surface(mSourceSurface);
+ return surface.forget();
+ }
+
+ if (!mDecodedBuffer) {
+ return PlanarYCbCrImage::GetAsSourceSurface();
+ }
+
+ gfxImageFormat format = GetOffscreenFormat();
+
+ RefPtr<gfx::SourceSurface> surface;
+ {
+ // Create a DrawTarget so that we can own the data inside mDecodeBuffer.
+ // We create the target out of mDecodedBuffer, and get a snapshot from it.
+ // The draw target is destroyed on scope exit and the surface owns the data.
+ RefPtr<gfx::DrawTarget> drawTarget = gfxPlatform::CreateDrawTargetForData(
+ mDecodedBuffer.get(), mSize, mStride,
+ gfx::ImageFormatToSurfaceFormat(format));
+ if (!drawTarget) {
+ return nullptr;
+ }
+
+ surface = drawTarget->Snapshot();
+ }
+
+ mRecycleBin->RecycleBuffer(std::move(mDecodedBuffer), mSize.height * mStride);
+
+ mSourceSurface = surface;
+ return surface.forget();
+}
+
+ImageFactory* BasicLayerManager::GetImageFactory() {
+ if (!mFactory) {
+ mFactory = new BasicImageFactory();
+ }
+
+ return mFactory.get();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/basic/BasicImplData.h b/gfx/layers/basic/BasicImplData.h
new file mode 100644
index 0000000000..6d0a7980a9
--- /dev/null
+++ b/gfx/layers/basic/BasicImplData.h
@@ -0,0 +1,140 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef GFX_BASICIMPLDATA_H
+#define GFX_BASICIMPLDATA_H
+
+#include "mozilla/AlreadyAddRefed.h" // for already_AddRefed
+#include "mozilla/gfx/Point.h" // for Point
+#include "mozilla/gfx/Types.h" // for CompositionOp, CompositionOp::OP_OVER, CompositionOp::OP_SOURCE
+#include "mozilla/layers/LayerManager.h" // for LayerManager, LayerManager::DrawPaintedLayerCallback
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupports.h" // for MOZ_COUNTED_DTOR_VIRTUAL, MOZ_COUNT_CTOR
+
+class gfxContext;
+
+namespace mozilla {
+namespace gfx {
+class DrawTarget;
+class SourceSurface;
+} // namespace gfx
+
+namespace layers {
+
+class Layer;
+class ReadbackProcessor;
+
+/**
+ * This is the ImplData for all Basic layers. It also exposes methods
+ * private to the Basic implementation that are common to all Basic layer types.
+ * In particular, there is an internal Paint() method that we can use
+ * to paint the contents of non-PaintedLayers.
+ *
+ * The class hierarchy for Basic layers is like this:
+ * BasicImplData
+ * Layer | | |
+ * | | | |
+ * +-> ContainerLayer | | |
+ * | | | | |
+ * | +-> BasicContainerLayer <--+ | |
+ * | | |
+ * +-> PaintedLayer | |
+ * | | | |
+ * | +-> BasicPaintedLayer <---------+ |
+ * | |
+ * +-> ImageLayer |
+ * | |
+ * +-> BasicImageLayer <--------------+
+ */
+class BasicImplData {
+ public:
+ BasicImplData()
+ : mHidden(false),
+ mClipToVisibleRegion(false),
+ mDrawAtomically(false),
+ mOperator(gfx::CompositionOp::OP_OVER) {
+ MOZ_COUNT_CTOR(BasicImplData);
+ }
+ MOZ_COUNTED_DTOR_VIRTUAL(BasicImplData)
+
+ /**
+ * Layers that paint themselves, such as ImageLayers, should paint
+ * in response to this method call. aContext will already have been
+ * set up to account for all the properties of the layer (transform,
+ * opacity, etc).
+ */
+ virtual void Paint(gfx::DrawTarget* aDT, const gfx::Point& aDeviceOffset,
+ Layer* aMaskLayer) {}
+
+ /**
+ * Like Paint() but called for PaintedLayers with the additional parameters
+ * they need.
+ * If mClipToVisibleRegion is set, then the layer must clip to its
+ * effective visible region (snapped or unsnapped, it doesn't matter).
+ */
+ virtual void PaintThebes(gfxContext* aContext, Layer* aMasklayer,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData) {}
+
+ virtual void Validate(LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData, ReadbackProcessor* aReadback) {}
+
+ /**
+ * Layers will get this call when their layer manager is destroyed, this
+ * indicates they should clear resources they don't really need after their
+ * LayerManager ceases to exist.
+ */
+ virtual void ClearCachedResources() {}
+
+ /**
+ * This variable is set by MarkLayersHidden() before painting. It indicates
+ * that the layer should not be composited during this transaction.
+ */
+ void SetHidden(bool aCovered) { mHidden = aCovered; }
+ bool IsHidden() const { return false; }
+ /**
+ * This variable is set by MarkLayersHidden() before painting. This is
+ * the operator to be used when compositing the layer in this transaction. It
+ * must be OVER or SOURCE.
+ */
+ void SetOperator(gfx::CompositionOp aOperator) {
+ NS_ASSERTION(aOperator == gfx::CompositionOp::OP_OVER ||
+ aOperator == gfx::CompositionOp::OP_SOURCE,
+ "Bad composition operator");
+ mOperator = aOperator;
+ }
+
+ gfx::CompositionOp GetOperator() const { return mOperator; }
+
+ /**
+ * Return a surface for this layer. Will use an existing surface, if
+ * possible, or may create a temporary surface. Implement this
+ * method for any layers that might be used as a mask. Should only
+ * return false if a surface cannot be created. If true is
+ * returned, only one of |aSurface| or |aDescriptor| is valid.
+ */
+ virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() {
+ return nullptr;
+ }
+
+ bool GetClipToVisibleRegion() { return mClipToVisibleRegion; }
+ void SetClipToVisibleRegion(bool aClip) { mClipToVisibleRegion = aClip; }
+
+ void SetDrawAtomically(bool aDrawAtomically) {
+ mDrawAtomically = aDrawAtomically;
+ }
+
+ protected:
+ bool mHidden;
+ bool mClipToVisibleRegion;
+ bool mDrawAtomically;
+ gfx::CompositionOp mOperator;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/basic/BasicLayerManager.cpp b/gfx/layers/basic/BasicLayerManager.cpp
new file mode 100644
index 0000000000..83bc8cdffc
--- /dev/null
+++ b/gfx/layers/basic/BasicLayerManager.cpp
@@ -0,0 +1,969 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 <stdint.h> // for uint32_t
+#include <stdlib.h> // for rand, RAND_MAX
+#include <sys/types.h> // for int32_t
+#include <stack> // for stack
+#include "BasicContainerLayer.h" // for BasicContainerLayer
+#include "BasicLayersImpl.h" // for ToData, BasicReadbackLayer, etc
+#include "GeckoProfiler.h" // for AUTO_PROFILER_LABEL
+#include "ImageContainer.h" // for ImageFactory
+#include "Layers.h" // for Layer, ContainerLayer, etc
+#include "ReadbackLayer.h" // for ReadbackLayer
+#include "ReadbackProcessor.h" // for ReadbackProcessor
+#include "RenderTrace.h" // for RenderTraceLayers, etc
+#include "basic/BasicImplData.h" // for BasicImplData
+#include "basic/BasicLayers.h" // for BasicLayerManager, etc
+#include "gfxASurface.h" // for gfxASurface, etc
+#include "gfxContext.h" // for gfxContext, etc
+#include "gfxImageSurface.h" // for gfxImageSurface
+#include "gfxMatrix.h" // for gfxMatrix
+#include "gfxPlatform.h" // for gfxPlatform
+
+#include "gfxPoint.h" // for IntSize, gfxPoint
+#include "gfxRect.h" // for gfxRect
+#include "gfxUtils.h" // for gfxUtils
+#include "gfx2DGlue.h" // for thebes --> moz2d transition
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/StaticPrefs_nglayout.h"
+#include "mozilla/WidgetUtils.h" // for ScreenRotation
+#include "mozilla/gfx/2D.h" // for DrawTarget
+#include "mozilla/gfx/BasePoint.h" // for BasePoint
+#include "mozilla/gfx/BaseRect.h" // for BaseRect
+#include "mozilla/gfx/Matrix.h" // for Matrix
+#include "mozilla/gfx/PathHelpers.h"
+#include "mozilla/gfx/Rect.h" // for IntRect, Rect
+#include "mozilla/layers/BSPTree.h"
+#include "mozilla/layers/LayersTypes.h" // for BufferMode::BUFFER_NONE, etc
+#include "mozilla/mozalloc.h" // for operator new
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ASSERTION, etc
+#include "nsISupportsImpl.h" // for gfxContext::Release, etc
+#include "nsLayoutUtils.h" // for nsLayoutUtils
+#include "nsPoint.h" // for nsIntPoint
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsRegion.h" // for nsIntRegion, etc
+#include "nsTArray.h" // for AutoTArray
+#include "TreeTraversal.h" // for ForEachNode
+
+class nsIWidget;
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+/**
+ * Clips to the smallest device-pixel-aligned rectangle containing aRect
+ * in user space.
+ * Returns true if the clip is "perfect", i.e. we actually clipped exactly to
+ * aRect.
+ */
+static bool ClipToContain(gfxContext* aContext, const IntRect& aRect) {
+ gfxRect userRect(aRect.X(), aRect.Y(), aRect.Width(), aRect.Height());
+ gfxRect deviceRect = aContext->UserToDevice(userRect);
+ deviceRect.RoundOut();
+
+ Matrix currentMatrix = aContext->CurrentMatrix();
+ aContext->SetMatrix(Matrix());
+ aContext->NewPath();
+ aContext->Rectangle(deviceRect);
+ aContext->Clip();
+ aContext->SetMatrix(currentMatrix);
+
+ return aContext->DeviceToUser(deviceRect).IsEqualInterior(userRect);
+}
+
+bool BasicLayerManager::PushGroupForLayer(gfxContext* aContext, Layer* aLayer,
+ const nsIntRegion& aRegion,
+ PushedGroup& aGroupResult) {
+ aGroupResult.mVisibleRegion = aRegion;
+ aGroupResult.mFinalTarget = aContext;
+ aGroupResult.mOperator = GetEffectiveOperator(aLayer);
+ aGroupResult.mOpacity = aLayer->GetEffectiveOpacity();
+
+ // If we need to call PushGroup, we should clip to the smallest possible
+ // area first to minimize the size of the temporary surface.
+ bool didCompleteClip = ClipToContain(aContext, aRegion.GetBounds());
+
+ bool canPushGroup =
+ aGroupResult.mOperator == CompositionOp::OP_OVER ||
+ (aGroupResult.mOperator == CompositionOp::OP_SOURCE &&
+ (aLayer->CanUseOpaqueSurface() ||
+ aLayer->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA));
+
+ if (!canPushGroup) {
+ aContext->Save();
+ gfxUtils::ClipToRegion(aGroupResult.mFinalTarget,
+ aGroupResult.mVisibleRegion);
+
+ // PushGroup/PopGroup do not support non operator over.
+ gfxRect rect = aContext->GetClipExtents(gfxContext::eDeviceSpace);
+ rect.RoundOut();
+ IntRect surfRect;
+ ToRect(rect).ToIntRect(&surfRect);
+
+ if (!surfRect.IsEmpty()) {
+ RefPtr<DrawTarget> dt;
+ if (aContext->GetDrawTarget()->CanCreateSimilarDrawTarget(
+ surfRect.Size(), SurfaceFormat::B8G8R8A8)) {
+ dt = aContext->GetDrawTarget()->CreateSimilarDrawTarget(
+ surfRect.Size(), SurfaceFormat::B8G8R8A8);
+ }
+
+ RefPtr<gfxContext> ctx =
+ gfxContext::CreateOrNull(dt, ToRect(rect).TopLeft());
+ if (!ctx) {
+ gfxCriticalNote
+ << "BasicLayerManager context problem in PushGroupForLayer "
+ << gfx::hexa(dt);
+ return false;
+ }
+ ctx->SetMatrix(aContext->CurrentMatrix());
+
+ aGroupResult.mGroupOffset = surfRect.TopLeft();
+ aGroupResult.mGroupTarget = ctx;
+
+ aGroupResult.mMaskSurface =
+ GetMaskForLayer(aLayer, &aGroupResult.mMaskTransform);
+ return true;
+ }
+ aContext->Restore();
+ }
+
+ Matrix maskTransform;
+ RefPtr<SourceSurface> maskSurf = GetMaskForLayer(aLayer, &maskTransform);
+
+ if (maskSurf) {
+ // The returned transform will transform the mask to device space on the
+ // destination. Since the User->Device space transform will be applied
+ // to the mask by PopGroupAndBlend we need to adjust the transform to
+ // transform the mask to user space.
+ Matrix currentTransform = aGroupResult.mFinalTarget->CurrentMatrix();
+ currentTransform.Invert();
+ maskTransform = maskTransform * currentTransform;
+ }
+
+ if (aLayer->CanUseOpaqueSurface() &&
+ ((didCompleteClip && aRegion.GetNumRects() == 1) ||
+ !aContext->CurrentMatrix().HasNonIntegerTranslation())) {
+ // If the layer is opaque in its visible region we can push a
+ // gfxContentType::COLOR group. We need to make sure that only pixels inside
+ // the layer's visible region are copied back to the destination. Remember
+ // if we've already clipped precisely to the visible region.
+ aGroupResult.mNeedsClipToVisibleRegion =
+ !didCompleteClip || aRegion.GetNumRects() > 1;
+ if (aGroupResult.mNeedsClipToVisibleRegion) {
+ aGroupResult.mFinalTarget->Save();
+ gfxUtils::ClipToRegion(aGroupResult.mFinalTarget,
+ aGroupResult.mVisibleRegion);
+ }
+
+ aContext->PushGroupForBlendBack(
+ gfxContentType::COLOR, aGroupResult.mOpacity, maskSurf, maskTransform);
+ } else {
+ if (aLayer->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA) {
+ aContext->PushGroupAndCopyBackground(gfxContentType::COLOR_ALPHA,
+ aGroupResult.mOpacity, maskSurf,
+ maskTransform);
+ } else {
+ aContext->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA,
+ aGroupResult.mOpacity, maskSurf,
+ maskTransform);
+ }
+ }
+
+ aGroupResult.mGroupTarget = aGroupResult.mFinalTarget;
+
+ return true;
+}
+
+void BasicLayerManager::PopGroupForLayer(PushedGroup& group) {
+ if (group.mFinalTarget == group.mGroupTarget) {
+ group.mFinalTarget->PopGroupAndBlend();
+ if (group.mNeedsClipToVisibleRegion) {
+ group.mFinalTarget->Restore();
+ }
+ return;
+ }
+
+ DrawTarget* dt = group.mFinalTarget->GetDrawTarget();
+ RefPtr<DrawTarget> sourceDT = group.mGroupTarget->GetDrawTarget();
+ group.mGroupTarget = nullptr;
+
+ RefPtr<SourceSurface> src = sourceDT->Snapshot();
+
+ if (group.mMaskSurface) {
+ Point finalOffset = group.mFinalTarget->GetDeviceOffset();
+ dt->SetTransform(group.mMaskTransform * Matrix::Translation(-finalOffset));
+ Matrix surfTransform = group.mMaskTransform;
+ surfTransform.Invert();
+ dt->MaskSurface(SurfacePattern(src, ExtendMode::CLAMP,
+ surfTransform * Matrix::Translation(
+ group.mGroupOffset.x,
+ group.mGroupOffset.y)),
+ group.mMaskSurface, Point(0, 0),
+ DrawOptions(group.mOpacity, group.mOperator));
+ } else {
+ // For now this is required since our group offset is in device space of the
+ // final target, context but that may still have its own device offset. Once
+ // PushGroup/PopGroup logic is migrated to DrawTargets this can go as
+ // gfxContext::GetDeviceOffset will essentially always become null.
+ dt->SetTransform(
+ Matrix::Translation(-group.mFinalTarget->GetDeviceOffset()));
+ dt->DrawSurface(src,
+ Rect(group.mGroupOffset.x, group.mGroupOffset.y,
+ src->GetSize().width, src->GetSize().height),
+ Rect(0, 0, src->GetSize().width, src->GetSize().height),
+ DrawSurfaceOptions(SamplingFilter::POINT),
+ DrawOptions(group.mOpacity, group.mOperator));
+ }
+
+ if (group.mNeedsClipToVisibleRegion) {
+ dt->PopClip();
+ }
+
+ group.mFinalTarget->Restore();
+}
+
+static IntRect ToInsideIntRect(const gfxRect& aRect) {
+ return IntRect::RoundIn(aRect.X(), aRect.Y(), aRect.Width(), aRect.Height());
+}
+
+// A context helper for BasicLayerManager::PaintLayer() that holds all the
+// painting context together in a data structure so it can be easily passed
+// around. It also uses ensures that the Transform and Opaque rect are restored
+// to their former state on destruction.
+
+class PaintLayerContext {
+ public:
+ PaintLayerContext(gfxContext* aTarget, Layer* aLayer,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData)
+ : mTarget(aTarget),
+ mTargetMatrixSR(aTarget),
+ mLayer(aLayer),
+ mCallback(aCallback),
+ mCallbackData(aCallbackData),
+ mPushedOpaqueRect(false) {}
+
+ ~PaintLayerContext() {
+ // Matrix is restored by mTargetMatrixSR
+ if (mPushedOpaqueRect) {
+ ClearOpaqueRect();
+ }
+ }
+
+ // Gets the effective transform and returns true if it is a 2D
+ // transform.
+ bool Setup2DTransform() {
+ // Will return an identity matrix for 3d transforms.
+ return mLayer->GetEffectiveTransformForBuffer().CanDraw2D(&mTransform);
+ }
+
+ // Applies the effective transform if it's 2D. If it's a 3D transform then
+ // it applies an identity.
+ void Apply2DTransform() { mTarget->SetMatrix(mTransform); }
+
+ // Set the opaque rect to match the bounds of the visible region.
+ void AnnotateOpaqueRect() {
+ const nsIntRegion visibleRegion =
+ mLayer->GetLocalVisibleRegion().ToUnknownRegion();
+ const IntRect& bounds = visibleRegion.GetBounds();
+
+ DrawTarget* dt = mTarget->GetDrawTarget();
+ const IntRect& targetOpaqueRect = dt->GetOpaqueRect();
+
+ // Try to annotate currentSurface with a region of pixels that have been
+ // (or will be) painted opaque, if no such region is currently set.
+ if (targetOpaqueRect.IsEmpty() && visibleRegion.GetNumRects() == 1 &&
+ (mLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) &&
+ !mTransform.HasNonAxisAlignedTransform()) {
+ gfx::Rect opaqueRect = dt->GetTransform().TransformBounds(
+ gfx::Rect(bounds.X(), bounds.Y(), bounds.Width(), bounds.Height()));
+ opaqueRect.RoundIn();
+ IntRect intOpaqueRect;
+ if (opaqueRect.ToIntRect(&intOpaqueRect)) {
+ mTarget->GetDrawTarget()->SetOpaqueRect(intOpaqueRect);
+ mPushedOpaqueRect = true;
+ }
+ }
+ }
+
+ // Clear the Opaque rect. Although this doesn't really restore it to it's
+ // previous state it will happen on the exit path of the PaintLayer() so when
+ // painting is complete the opaque rect qill be clear.
+ void ClearOpaqueRect() { mTarget->GetDrawTarget()->SetOpaqueRect(IntRect()); }
+
+ gfxContext* mTarget;
+ gfxContextMatrixAutoSaveRestore mTargetMatrixSR;
+ Layer* mLayer;
+ LayerManager::DrawPaintedLayerCallback mCallback;
+ void* mCallbackData;
+ Matrix mTransform;
+ bool mPushedOpaqueRect;
+};
+
+BasicLayerManager::BasicLayerManager(nsIWidget* aWidget)
+ : mPhase(PHASE_NONE),
+ mWidget(aWidget),
+ mDoubleBuffering(BufferMode::BUFFER_NONE),
+ mType(BLM_WIDGET),
+ mUsingDefaultTarget(false),
+ mTransactionIncomplete(false),
+ mCompositorMightResample(false) {
+ MOZ_COUNT_CTOR(BasicLayerManager);
+ NS_ASSERTION(aWidget, "Must provide a widget");
+}
+
+BasicLayerManager::BasicLayerManager(BasicLayerManagerType aType)
+ : mPhase(PHASE_NONE),
+ mWidget(nullptr),
+ mDoubleBuffering(BufferMode::BUFFER_NONE),
+ mType(aType),
+ mUsingDefaultTarget(false),
+ mTransactionIncomplete(false),
+ mCompositorMightResample(false) {
+ MOZ_COUNT_CTOR(BasicLayerManager);
+ MOZ_ASSERT(mType != BLM_WIDGET);
+}
+
+BasicLayerManager::~BasicLayerManager() {
+ NS_ASSERTION(!InTransaction(), "Died during transaction?");
+
+ ClearCachedResources();
+
+ mRoot = nullptr;
+
+ MOZ_COUNT_DTOR(BasicLayerManager);
+}
+
+void BasicLayerManager::SetDefaultTarget(gfxContext* aContext) {
+ NS_ASSERTION(!InTransaction(), "Must set default target outside transaction");
+ mDefaultTarget = aContext;
+}
+
+void BasicLayerManager::SetDefaultTargetConfiguration(
+ BufferMode aDoubleBuffering, ScreenRotation aRotation) {
+ mDoubleBuffering = aDoubleBuffering;
+}
+
+bool BasicLayerManager::BeginTransaction(const nsCString& aURL) {
+ mInTransaction = true;
+ mUsingDefaultTarget = true;
+ return BeginTransactionWithTarget(mDefaultTarget, aURL);
+}
+
+bool BasicLayerManager::BeginTransactionWithTarget(gfxContext* aTarget,
+ const nsCString& aURL) {
+ mInTransaction = true;
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ MOZ_LAYERS_LOG(("[----- BeginTransaction"));
+ Log();
+#endif
+
+ NS_ASSERTION(!InTransaction(), "Nested transactions not allowed");
+ mPhase = PHASE_CONSTRUCTION;
+ mTarget = aTarget;
+ return true;
+}
+
+static void TransformIntRect(IntRect& aRect, const Matrix& aMatrix,
+ IntRect (*aRoundMethod)(const gfxRect&)) {
+ Rect gr = Rect(aRect.X(), aRect.Y(), aRect.Width(), aRect.Height());
+ gr = aMatrix.TransformBounds(gr);
+ aRect = (*aRoundMethod)(ThebesRect(gr));
+}
+
+/**
+ * This function assumes that GetEffectiveTransform transforms
+ * all layers to the same coordinate system (the "root coordinate system").
+ * It can't be used as is by accelerated layers because of intermediate
+ * surfaces. This must set the hidden flag to true or false on *all* layers in
+ * the subtree. It also sets the operator for all layers to "OVER", and call
+ * SetDrawAtomically(false).
+ * It clears mClipToVisibleRegion on all layers.
+ * @param aClipRect the cliprect, in the root coordinate system. We assume
+ * that any layer drawing is clipped to this rect. It is therefore not
+ * allowed to add to the opaque region outside that rect.
+ * @param aDirtyRect the dirty rect that will be painted, in the root
+ * coordinate system. Layers outside this rect should be hidden.
+ * @param aOpaqueRegion the opaque region covering aLayer, in the
+ * root coordinate system.
+ */
+enum {
+ ALLOW_OPAQUE = 0x01,
+};
+static void MarkLayersHidden(Layer* aLayer, const IntRect& aClipRect,
+ const IntRect& aDirtyRect,
+ nsIntRegion& aOpaqueRegion, uint32_t aFlags) {
+ IntRect newClipRect(aClipRect);
+ uint32_t newFlags = aFlags;
+
+ // Allow aLayer or aLayer's descendants to cover underlying layers
+ // only if it's opaque.
+ if (aLayer->GetOpacity() != 1.0f) {
+ newFlags &= ~ALLOW_OPAQUE;
+ }
+
+ {
+ const Maybe<ParentLayerIntRect>& clipRect = aLayer->GetLocalClipRect();
+ if (clipRect) {
+ IntRect cr = clipRect->ToUnknownRect();
+ // clipRect is in the container's coordinate system. Get it into the
+ // global coordinate system.
+ if (aLayer->GetParent()) {
+ Matrix tr;
+ if (aLayer->GetParent()->GetEffectiveTransform().CanDraw2D(&tr)) {
+ // Clip rect is applied after aLayer's transform, i.e., in the
+ // coordinate system of aLayer's parent.
+ TransformIntRect(cr, tr, ToInsideIntRect);
+ } else {
+ cr.SetRect(0, 0, 0, 0);
+ }
+ }
+ newClipRect.IntersectRect(newClipRect, cr);
+ }
+ }
+
+ BasicImplData* data = ToData(aLayer);
+ data->SetOperator(CompositionOp::OP_OVER);
+ data->SetClipToVisibleRegion(false);
+ data->SetDrawAtomically(false);
+
+ if (!aLayer->AsContainerLayer()) {
+ Matrix transform;
+ if (!aLayer->GetEffectiveTransform().CanDraw2D(&transform)) {
+ data->SetHidden(false);
+ return;
+ }
+
+ nsIntRegion region = aLayer->GetLocalVisibleRegion().ToUnknownRegion();
+ IntRect r = region.GetBounds();
+ TransformIntRect(r, transform, ToOutsideIntRect);
+ r.IntersectRect(r, aDirtyRect);
+ data->SetHidden(aOpaqueRegion.Contains(r));
+
+ // Allow aLayer to cover underlying layers only if aLayer's
+ // content is opaque
+ if ((aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) &&
+ (newFlags & ALLOW_OPAQUE)) {
+ for (auto iter = region.RectIter(); !iter.Done(); iter.Next()) {
+ r = iter.Get();
+ TransformIntRect(r, transform, ToInsideIntRect);
+
+ r.IntersectRect(r, newClipRect);
+ aOpaqueRegion.Or(aOpaqueRegion, r);
+ }
+ }
+ } else {
+ Layer* child = aLayer->GetLastChild();
+ bool allHidden = true;
+ for (; child; child = child->GetPrevSibling()) {
+ MarkLayersHidden(child, newClipRect, aDirtyRect, aOpaqueRegion, newFlags);
+ if (!ToData(child)->IsHidden()) {
+ allHidden = false;
+ }
+ }
+ data->SetHidden(allHidden);
+ }
+}
+
+/**
+ * This function assumes that GetEffectiveTransform transforms
+ * all layers to the same coordinate system (the "root coordinate system").
+ * MarkLayersHidden must be called before calling this.
+ * @param aVisibleRect the rectangle of aLayer that is visible (i.e. not
+ * clipped and in the dirty rect), in the root coordinate system.
+ */
+static void ApplyDoubleBuffering(Layer* aLayer, const IntRect& aVisibleRect) {
+ BasicImplData* data = ToData(aLayer);
+ if (data->IsHidden()) return;
+
+ IntRect newVisibleRect(aVisibleRect);
+
+ {
+ const Maybe<ParentLayerIntRect>& clipRect = aLayer->GetLocalClipRect();
+ if (clipRect) {
+ IntRect cr = clipRect->ToUnknownRect();
+ // clipRect is in the container's coordinate system. Get it into the
+ // global coordinate system.
+ if (aLayer->GetParent()) {
+ Matrix tr;
+ if (aLayer->GetParent()->GetEffectiveTransform().CanDraw2D(&tr)) {
+ NS_ASSERTION(!ThebesMatrix(tr).HasNonIntegerTranslation(),
+ "Parent can only have an integer translation");
+ cr += nsIntPoint(int32_t(tr._31), int32_t(tr._32));
+ } else {
+ NS_ERROR("Parent can only have an integer translation");
+ }
+ }
+ newVisibleRect.IntersectRect(newVisibleRect, cr);
+ }
+ }
+
+ BasicContainerLayer* container =
+ static_cast<BasicContainerLayer*>(aLayer->AsContainerLayer());
+ // Layers that act as their own backbuffers should be drawn to the destination
+ // using OP_SOURCE to ensure that alpha values in a transparent window are
+ // cleared. This can also be faster than OP_OVER.
+ if (!container) {
+ data->SetOperator(CompositionOp::OP_SOURCE);
+ data->SetDrawAtomically(true);
+ } else {
+ if (container->UseIntermediateSurface() ||
+ !container->ChildrenPartitionVisibleRegion(newVisibleRect)) {
+ // We need to double-buffer this container.
+ data->SetOperator(CompositionOp::OP_SOURCE);
+ container->ForceIntermediateSurface();
+ } else {
+ // Tell the children to clip to their visible regions so our assumption
+ // that they don't paint outside their visible regions is valid!
+ for (Layer* child = aLayer->GetFirstChild(); child;
+ child = child->GetNextSibling()) {
+ ToData(child)->SetClipToVisibleRegion(true);
+ ApplyDoubleBuffering(child, newVisibleRect);
+ }
+ }
+ }
+}
+
+void BasicLayerManager::EndTransaction(DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ EndTransactionFlags aFlags) {
+ mInTransaction = false;
+
+ EndTransactionInternal(aCallback, aCallbackData, aFlags);
+}
+
+void BasicLayerManager::AbortTransaction() {
+ NS_ASSERTION(InConstruction(), "Should be in construction phase");
+ mPhase = PHASE_NONE;
+ mUsingDefaultTarget = false;
+ mInTransaction = false;
+}
+
+bool BasicLayerManager::EndTransactionInternal(
+ DrawPaintedLayerCallback aCallback, void* aCallbackData,
+ EndTransactionFlags aFlags) {
+ AUTO_PROFILER_LABEL("BasicLayerManager::EndTransactionInternal", GRAPHICS);
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ MOZ_LAYERS_LOG((" ----- (beginning paint)"));
+ Log();
+#endif
+
+ NS_ASSERTION(InConstruction(), "Should be in construction phase");
+ mPhase = PHASE_DRAWING;
+
+ SetCompositionTime(TimeStamp::Now());
+
+ RenderTraceLayers(mRoot, "FF00");
+
+ mTransactionIncomplete = false;
+
+ std::unordered_set<ScrollableLayerGuid::ViewID> scrollIdsUpdated;
+
+ if (mRoot) {
+ if (aFlags & END_NO_COMPOSITE) {
+ // Apply pending tree updates before recomputing effective
+ // properties.
+ scrollIdsUpdated = mRoot->ApplyPendingUpdatesToSubtree();
+ }
+
+ // Need to do this before we call ApplyDoubleBuffering,
+ // which depends on correct effective transforms
+ if (mTarget) {
+ mSnapEffectiveTransforms =
+ !mTarget->GetDrawTarget()->GetUserData(&sDisablePixelSnapping);
+ } else {
+ mSnapEffectiveTransforms = true;
+ }
+ mRoot->ComputeEffectiveTransforms(
+ mTarget ? Matrix4x4::From2D(mTarget->CurrentMatrix()) : Matrix4x4());
+
+ ToData(mRoot)->Validate(aCallback, aCallbackData, nullptr);
+ if (mRoot->GetMaskLayer()) {
+ ToData(mRoot->GetMaskLayer())
+ ->Validate(aCallback, aCallbackData, nullptr);
+ }
+ }
+
+ if (mTarget && mRoot && !(aFlags & END_NO_IMMEDIATE_REDRAW) &&
+ !(aFlags & END_NO_COMPOSITE)) {
+ IntRect clipRect =
+ ToOutsideIntRect(mTarget->GetClipExtents(gfxContext::eDeviceSpace));
+
+ if (IsRetained()) {
+ nsIntRegion region;
+ MarkLayersHidden(mRoot, clipRect, clipRect, region, ALLOW_OPAQUE);
+ if (mUsingDefaultTarget && mDoubleBuffering != BufferMode::BUFFER_NONE) {
+ ApplyDoubleBuffering(mRoot, clipRect);
+ }
+ }
+
+ PaintLayer(mTarget, mRoot, aCallback, aCallbackData);
+ if (!mRegionToClear.IsEmpty()) {
+ for (auto iter = mRegionToClear.RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& r = iter.Get();
+ mTarget->GetDrawTarget()->ClearRect(
+ Rect(r.X(), r.Y(), r.Width(), r.Height()));
+ }
+ }
+ if (mWidget) {
+ FlashWidgetUpdateArea(mTarget);
+ }
+ RecordFrame();
+
+ if (!mTransactionIncomplete) {
+ // Clear out target if we have a complete transaction.
+ mTarget = nullptr;
+ }
+ }
+
+ if (mRoot) {
+ mAnimationReadyTime = TimeStamp::Now();
+ mRoot->StartPendingAnimations(mAnimationReadyTime);
+
+ // Once we're sure we're not going to fall back to a full paint,
+ // notify the scroll frames which had pending updates.
+ if (!mTransactionIncomplete) {
+ for (ScrollableLayerGuid::ViewID scrollId : scrollIdsUpdated) {
+ nsLayoutUtils::NotifyPaintSkipTransaction(scrollId);
+ }
+ }
+ }
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ Log();
+ MOZ_LAYERS_LOG(("]----- EndTransaction"));
+#endif
+
+ // Go back to the construction phase if the transaction isn't complete.
+ // Layout will update the layer tree and call EndTransaction().
+ mPhase = mTransactionIncomplete ? PHASE_CONSTRUCTION : PHASE_NONE;
+
+ if (!mTransactionIncomplete) {
+ // This is still valid if the transaction was incomplete.
+ mUsingDefaultTarget = false;
+ }
+
+ NS_ASSERTION(!aCallback || !mTransactionIncomplete,
+ "If callback is not null, transaction must be complete");
+
+ // XXX - We should probably assert here that for an incomplete transaction
+ // out target is the default target.
+
+ return !mTransactionIncomplete;
+}
+
+void BasicLayerManager::FlashWidgetUpdateArea(gfxContext* aContext) {
+ if (StaticPrefs::nglayout_debug_widget_update_flashing()) {
+ float r = float(rand()) / float(RAND_MAX);
+ float g = float(rand()) / float(RAND_MAX);
+ float b = float(rand()) / float(RAND_MAX);
+ aContext->SetDeviceColor(DeviceColor(r, g, b, 0.2f));
+ aContext->Paint();
+ }
+}
+
+bool BasicLayerManager::EndEmptyTransaction(EndTransactionFlags aFlags) {
+ mInTransaction = false;
+
+ if (!mRoot) {
+ return false;
+ }
+
+ return EndTransactionInternal(nullptr, nullptr, aFlags);
+}
+
+void BasicLayerManager::SetRoot(Layer* aLayer) {
+ NS_ASSERTION(aLayer, "Root can't be null");
+ NS_ASSERTION(aLayer->Manager() == this, "Wrong manager");
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ mRoot = aLayer;
+}
+
+void BasicLayerManager::PaintSelfOrChildren(PaintLayerContext& aPaintContext,
+ gfxContext* aGroupTarget) {
+ MOZ_ASSERT(aGroupTarget);
+ BasicImplData* data = ToData(aPaintContext.mLayer);
+
+ /* Only paint ourself, or our children - This optimization relies on this! */
+ Layer* child = aPaintContext.mLayer->GetFirstChild();
+ if (!child) {
+ if (aPaintContext.mLayer->AsPaintedLayer()) {
+ data->PaintThebes(aGroupTarget, aPaintContext.mLayer->GetMaskLayer(),
+ aPaintContext.mCallback, aPaintContext.mCallbackData);
+ } else {
+ data->Paint(aGroupTarget->GetDrawTarget(),
+ aGroupTarget->GetDeviceOffset(),
+ aPaintContext.mLayer->GetMaskLayer());
+ }
+ } else {
+ ContainerLayer* container =
+ static_cast<ContainerLayer*>(aPaintContext.mLayer);
+
+ nsTArray<LayerPolygon> children = container->SortChildrenBy3DZOrder(
+ ContainerLayer::SortMode::WITHOUT_GEOMETRY);
+
+ for (uint32_t i = 0; i < children.Length(); i++) {
+ Layer* layer = children.ElementAt(i).layer;
+ if (layer->IsBackfaceHidden()) {
+ continue;
+ }
+ if (!layer->AsContainerLayer() && !layer->IsVisible()) {
+ continue;
+ }
+
+ PaintLayer(aGroupTarget, layer, aPaintContext.mCallback,
+ aPaintContext.mCallbackData);
+ if (mTransactionIncomplete) break;
+ }
+ }
+}
+
+void BasicLayerManager::FlushGroup(PaintLayerContext& aPaintContext,
+ bool aNeedsClipToVisibleRegion) {
+ // If we're doing our own double-buffering, we need to avoid drawing
+ // the results of an incomplete transaction to the destination surface ---
+ // that could cause flicker. Double-buffering is implemented using a
+ // temporary surface for one or more container layers, so we need to stop
+ // those temporary surfaces from being composited to aTarget.
+ // ApplyDoubleBuffering guarantees that this container layer can't
+ // intersect any other leaf layers, so if the transaction is not yet marked
+ // incomplete, the contents of this container layer are the final contents
+ // for the window.
+ if (!mTransactionIncomplete) {
+ if (aNeedsClipToVisibleRegion) {
+ gfxUtils::ClipToRegion(
+ aPaintContext.mTarget,
+ aPaintContext.mLayer->GetLocalVisibleRegion().ToUnknownRegion());
+ }
+
+ CompositionOp op = GetEffectiveOperator(aPaintContext.mLayer);
+ AutoSetOperator setOperator(aPaintContext.mTarget, op);
+
+ PaintWithMask(aPaintContext.mTarget,
+ aPaintContext.mLayer->GetEffectiveOpacity(),
+ aPaintContext.mLayer->GetMaskLayer());
+ }
+}
+
+/**
+ * Install the clip applied to the layer on the given gfxContext. The
+ * given gfxContext is the buffer that the layer will be painted to.
+ */
+static void InstallLayerClipPreserves3D(gfxContext* aTarget, Layer* aLayer) {
+ const Maybe<ParentLayerIntRect>& clipRect = aLayer->GetLocalClipRect();
+
+ if (!clipRect) {
+ return;
+ }
+ MOZ_ASSERT(
+ !aLayer->Extend3DContext() || !aLayer->Combines3DTransformWithAncestors(),
+ "Layers in a preserve 3D context have no clip"
+ " except leaves and the estabisher!");
+
+ Layer* parent = aLayer->GetParent();
+ Matrix4x4 transform3d = parent && parent->Extend3DContext()
+ ? parent->GetEffectiveTransform()
+ : Matrix4x4();
+ Matrix transform;
+ if (!transform3d.CanDraw2D(&transform)) {
+ gfxDevCrash(LogReason::CannotDraw3D)
+ << "GFX: We should not have a 3D transform that CanDraw2D() is false!";
+ }
+ Matrix oldTransform = aTarget->CurrentMatrix();
+ transform *= oldTransform;
+ aTarget->SetMatrix(transform);
+
+ aTarget->SnappedClip(gfxRect(clipRect->X(), clipRect->Y(), clipRect->Width(),
+ clipRect->Height()));
+
+ aTarget->SetMatrix(oldTransform);
+}
+
+void BasicLayerManager::PaintLayer(gfxContext* aTarget, Layer* aLayer,
+ DrawPaintedLayerCallback aCallback,
+ void* aCallbackData) {
+ MOZ_ASSERT(aTarget);
+
+ AUTO_PROFILER_LABEL("BasicLayerManager::PaintLayer", GRAPHICS);
+
+ PaintLayerContext paintLayerContext(aTarget, aLayer, aCallback,
+ aCallbackData);
+
+ // Don't attempt to paint layers with a singular transform, cairo will
+ // just throw an error.
+ if (aLayer->GetEffectiveTransform().IsSingular()) {
+ return;
+ }
+
+ RenderTraceScope trace("BasicLayerManager::PaintLayer", "707070");
+
+ const Maybe<ParentLayerIntRect>& clipRect = aLayer->GetLocalClipRect();
+ BasicContainerLayer* container =
+ static_cast<BasicContainerLayer*>(aLayer->AsContainerLayer());
+ bool needsGroup = container && container->UseIntermediateSurface();
+ BasicImplData* data = ToData(aLayer);
+ bool needsClipToVisibleRegion =
+ data->GetClipToVisibleRegion() && !aLayer->AsPaintedLayer();
+ NS_ASSERTION(needsGroup || !container ||
+ container->GetOperator() == CompositionOp::OP_OVER,
+ "non-OVER operator should have forced UseIntermediateSurface");
+ NS_ASSERTION(
+ !container || !aLayer->GetMaskLayer() ||
+ container->UseIntermediateSurface(),
+ "ContainerLayer with mask layer should force UseIntermediateSurface");
+
+ gfxContextAutoSaveRestore contextSR;
+ gfxMatrix transform;
+ // Will return an identity matrix for 3d transforms, and is handled separately
+ // below.
+ bool is2D = paintLayerContext.Setup2DTransform();
+ MOZ_ASSERT(is2D || needsGroup || !container || container->Extend3DContext() ||
+ container->Is3DContextLeaf(),
+ "Must PushGroup for 3d transforms!");
+
+ Layer* parent = aLayer->GetParent();
+ bool inPreserves3DChain = parent && parent->Extend3DContext();
+ bool needsSaveRestore = needsGroup || clipRect || needsClipToVisibleRegion ||
+ !is2D || inPreserves3DChain;
+ if (needsSaveRestore) {
+ contextSR.SetContext(aTarget);
+
+ // The clips on ancestors on the preserved3d chain should be
+ // installed on the aTarget before painting the layer.
+ InstallLayerClipPreserves3D(aTarget, aLayer);
+ for (Layer* l = parent; l && l->Extend3DContext(); l = l->GetParent()) {
+ InstallLayerClipPreserves3D(aTarget, l);
+ }
+ }
+
+ paintLayerContext.Apply2DTransform();
+
+ nsIntRegion visibleRegion = aLayer->GetLocalVisibleRegion().ToUnknownRegion();
+ // If needsGroup is true, we'll clip to the visible region after we've popped
+ // the group
+ if (needsClipToVisibleRegion && !needsGroup) {
+ gfxUtils::ClipToRegion(aTarget, visibleRegion);
+ // Don't need to clip to visible region again
+ needsClipToVisibleRegion = false;
+ }
+
+ if (is2D) {
+ paintLayerContext.AnnotateOpaqueRect();
+ }
+
+ bool clipIsEmpty = aTarget->GetClipExtents().IsEmpty();
+ if (clipIsEmpty) {
+ PaintSelfOrChildren(paintLayerContext, aTarget);
+ return;
+ }
+
+ if (is2D) {
+ if (needsGroup) {
+ PushedGroup pushedGroup;
+ if (PushGroupForLayer(aTarget, aLayer,
+ aLayer->GetLocalVisibleRegion().ToUnknownRegion(),
+ pushedGroup)) {
+ PaintSelfOrChildren(paintLayerContext, pushedGroup.mGroupTarget);
+ PopGroupForLayer(pushedGroup);
+ }
+ } else {
+ PaintSelfOrChildren(paintLayerContext, aTarget);
+ }
+ } else {
+ if (!needsGroup && container) {
+ PaintSelfOrChildren(paintLayerContext, aTarget);
+ return;
+ }
+
+ IntRect bounds = visibleRegion.GetBounds();
+ // DrawTarget without the 3D transform applied:
+ RefPtr<DrawTarget> untransformedDT =
+ gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
+ IntSize(bounds.Width(), bounds.Height()), SurfaceFormat::B8G8R8A8);
+ if (!untransformedDT || !untransformedDT->IsValid()) {
+ return;
+ }
+ untransformedDT->SetTransform(
+ Matrix::Translation(-Point(bounds.X(), bounds.Y())));
+
+ RefPtr<gfxContext> groupTarget =
+ gfxContext::CreatePreservingTransformOrNull(untransformedDT);
+ MOZ_ASSERT(groupTarget); // already checked the target above
+
+ PaintSelfOrChildren(paintLayerContext, groupTarget);
+
+ // Temporary fast fix for bug 725886
+ // Revert these changes when 725886 is ready
+#ifdef DEBUG
+ if (aLayer->GetDebugColorIndex() != 0) {
+ DeviceColor color((aLayer->GetDebugColorIndex() & 1) ? 1.f : 0.f,
+ (aLayer->GetDebugColorIndex() & 2) ? 1.f : 0.f,
+ (aLayer->GetDebugColorIndex() & 4) ? 1.f : 0.f);
+ untransformedDT->FillRect(Rect(bounds), ColorPattern(color));
+ }
+#endif
+ Matrix4x4 effectiveTransform = aLayer->GetEffectiveTransform();
+ Rect xformBounds = effectiveTransform.TransformAndClipBounds(
+ Rect(bounds), ToRect(aTarget->GetClipExtents()));
+ xformBounds.RoundOut();
+ effectiveTransform.PostTranslate(-xformBounds.X(), -xformBounds.Y(), 0);
+ effectiveTransform.PreTranslate(bounds.X(), bounds.Y(), 0);
+
+ RefPtr<SourceSurface> untransformedSurf = untransformedDT->Snapshot();
+ RefPtr<DrawTarget> xformDT = untransformedDT->CreateSimilarDrawTarget(
+ IntSize::Truncate(xformBounds.Width(), xformBounds.Height()),
+ SurfaceFormat::B8G8R8A8);
+ RefPtr<SourceSurface> xformSurf;
+ if (xformDT && untransformedSurf &&
+ xformDT->Draw3DTransformedSurface(untransformedSurf,
+ effectiveTransform)) {
+ xformSurf = xformDT->Snapshot();
+ }
+
+ if (xformSurf) {
+ aTarget->SetPattern(new gfxPattern(
+ xformSurf, Matrix::Translation(xformBounds.TopLeft())));
+
+ // Azure doesn't support EXTEND_NONE, so to avoid extending the edges
+ // of the source surface out to the current clip region, clip to
+ // the rectangle of the result surface now.
+ aTarget->SnappedClip(ThebesRect(xformBounds));
+ FlushGroup(paintLayerContext, needsClipToVisibleRegion);
+ }
+ }
+}
+
+void BasicLayerManager::ClearCachedResources(Layer* aSubtree) {
+ MOZ_ASSERT(!aSubtree || aSubtree->Manager() == this);
+ if (aSubtree) {
+ ClearLayer(aSubtree);
+ } else if (mRoot) {
+ ClearLayer(mRoot);
+ }
+}
+void BasicLayerManager::ClearLayer(Layer* aLayer) {
+ ToData(aLayer)->ClearCachedResources();
+ for (Layer* child = aLayer->GetFirstChild(); child;
+ child = child->GetNextSibling()) {
+ ClearLayer(child);
+ }
+}
+
+already_AddRefed<ReadbackLayer> BasicLayerManager::CreateReadbackLayer() {
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ RefPtr<ReadbackLayer> layer = new BasicReadbackLayer(this);
+ return layer.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/basic/BasicLayers.h b/gfx/layers/basic/BasicLayers.h
new file mode 100644
index 0000000000..d313cea13c
--- /dev/null
+++ b/gfx/layers/basic/BasicLayers.h
@@ -0,0 +1,243 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef GFX_BASICLAYERS_H
+#define GFX_BASICLAYERS_H
+
+#include <stdint.h> // for INT32_MAX, int32_t
+#include "gfxTypes.h"
+#include "gfxContext.h" // for gfxContext
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/WidgetUtils.h" // for ScreenRotation
+#include "mozilla/layers/LayerManager.h" // for LayerManager::DrawPaintedLayerCallback, LayerManager::END_DEFAULT, LayerManager::EndTra...
+#include "mozilla/layers/LayersTypes.h" // for BufferMode, LayersBackend, etc
+#include "mozilla/TimeStamp.h"
+#include "nsAString.h"
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsISupportsImpl.h" // for gfxContext::AddRef, etc
+#include "nsRegion.h" // for nsIntRegion
+#include "nscore.h" // for nsAString, etc
+
+class nsIWidget;
+
+namespace mozilla {
+namespace layers {
+
+class CanvasLayer;
+class ColorLayer;
+class ContainerLayer;
+class ImageFactory;
+class ImageLayer;
+class Layer;
+class PaintLayerContext;
+class PaintedLayer;
+class ReadbackLayer;
+
+/**
+ * This is a cairo/Thebes-only, main-thread-only implementation of layers.
+ *
+ * In each transaction, the client sets up the layer tree and then during
+ * the drawing phase, each PaintedLayer is painted directly into the target
+ * context (with appropriate clipping and Push/PopGroups performed
+ * between layers).
+ */
+class BasicLayerManager final : public LayerManager {
+ public:
+ enum BasicLayerManagerType { BLM_WIDGET, BLM_OFFSCREEN, BLM_INACTIVE };
+ /**
+ * Construct a BasicLayerManager which will have no default
+ * target context. SetDefaultTarget or BeginTransactionWithTarget
+ * must be called for any rendering to happen. PaintedLayers will not
+ * be retained.
+ */
+ explicit BasicLayerManager(BasicLayerManagerType aType);
+ /**
+ * Construct a BasicLayerManager which will have no default
+ * target context. SetDefaultTarget or BeginTransactionWithTarget
+ * must be called for any rendering to happen. PaintedLayers will be
+ * retained; that is, we will try to retain the visible contents of
+ * PaintedLayers as cairo surfaces. We create PaintedLayer buffers by
+ * creating similar surfaces to the default target context, or to
+ * aWidget's GetThebesSurface if there is no default target context, or
+ * to the passed-in context if there is no widget and no default
+ * target context.
+ *
+ * This does not keep a strong reference to the widget, so the caller
+ * must ensure that the widget outlives the layer manager or call
+ * ClearWidget before the widget dies.
+ */
+ explicit BasicLayerManager(nsIWidget* aWidget);
+
+ protected:
+ virtual ~BasicLayerManager();
+
+ public:
+ BasicLayerManager* AsBasicLayerManager() override { return this; }
+
+ /**
+ * Set the default target context that will be used when BeginTransaction
+ * is called. This can only be called outside a transaction.
+ *
+ * aDoubleBuffering can request double-buffering for drawing to the
+ * default target. When BUFFERED, the layer manager avoids blitting
+ * temporary results to aContext and then overpainting them with final
+ * results, by using a temporary buffer when necessary. In BUFFERED
+ * mode we always completely overwrite the contents of aContext's
+ * destination surface (within the clip region) using OP_SOURCE.
+ */
+ void SetDefaultTarget(gfxContext* aContext);
+ virtual void SetDefaultTargetConfiguration(BufferMode aDoubleBuffering,
+ ScreenRotation aRotation);
+ gfxContext* GetDefaultTarget() { return mDefaultTarget; }
+
+ nsIWidget* GetRetainerWidget() { return mWidget; }
+ void ClearRetainerWidget() { mWidget = nullptr; }
+
+ virtual bool IsWidgetLayerManager() override { return mWidget != nullptr; }
+ virtual bool IsInactiveLayerManager() override {
+ return mType == BLM_INACTIVE;
+ }
+
+ virtual bool BeginTransaction(const nsCString& aURL = nsCString()) override;
+ virtual bool BeginTransactionWithTarget(
+ gfxContext* aTarget, const nsCString& aURL = nsCString()) override;
+ virtual bool EndEmptyTransaction(
+ EndTransactionFlags aFlags = END_DEFAULT) override;
+ virtual void EndTransaction(
+ DrawPaintedLayerCallback aCallback, void* aCallbackData,
+ EndTransactionFlags aFlags = END_DEFAULT) override;
+ void AbortTransaction();
+
+ virtual void SetRoot(Layer* aLayer) override;
+
+ virtual already_AddRefed<PaintedLayer> CreatePaintedLayer() override;
+ virtual already_AddRefed<ContainerLayer> CreateContainerLayer() override;
+ virtual already_AddRefed<ImageLayer> CreateImageLayer() override;
+ virtual already_AddRefed<CanvasLayer> CreateCanvasLayer() override;
+ virtual already_AddRefed<ColorLayer> CreateColorLayer() override;
+ virtual already_AddRefed<ReadbackLayer> CreateReadbackLayer() override;
+ virtual ImageFactory* GetImageFactory();
+
+ virtual LayersBackend GetBackendType() override {
+ return LayersBackend::LAYERS_BASIC;
+ }
+ virtual void GetBackendName(nsAString& name) override {
+ name.AssignLiteral("Basic");
+ }
+
+ bool InConstruction() { return mPhase == PHASE_CONSTRUCTION; }
+#ifdef DEBUG
+ bool InDrawing() { return mPhase == PHASE_DRAWING; }
+ bool InForward() { return mPhase == PHASE_FORWARD; }
+#endif
+ bool InTransaction() { return mPhase != PHASE_NONE; }
+
+ gfxContext* GetTarget() { return mTarget; }
+ void SetTarget(gfxContext* aTarget) {
+ mUsingDefaultTarget = false;
+ mTarget = aTarget;
+ }
+ bool IsRetained() { return mWidget != nullptr; }
+
+ virtual const char* Name() const override { return "Basic"; }
+
+ // Clear the cached contents of this layer tree.
+ virtual void ClearCachedResources(Layer* aSubtree = nullptr) override;
+
+ void SetTransactionIncomplete() { mTransactionIncomplete = true; }
+ bool IsTransactionIncomplete() { return mTransactionIncomplete; }
+
+ struct PushedGroup {
+ PushedGroup()
+ : mFinalTarget(nullptr),
+ mNeedsClipToVisibleRegion(false),
+ mOperator(gfx::CompositionOp::OP_COUNT),
+ mOpacity(0.0f) {}
+ gfxContext* mFinalTarget;
+ RefPtr<gfxContext> mGroupTarget;
+ nsIntRegion mVisibleRegion;
+ bool mNeedsClipToVisibleRegion;
+ gfx::IntPoint mGroupOffset;
+ gfx::CompositionOp mOperator;
+ gfx::Float mOpacity;
+ RefPtr<gfx::SourceSurface> mMaskSurface;
+ gfx::Matrix mMaskTransform;
+ };
+
+ // Construct a PushedGroup for a specific layer.
+ // Return false if it has some errors in PushGroupForLayer(). Then, the
+ // "aGroupResult" is unavailable for future using.
+ bool PushGroupForLayer(gfxContext* aContext, Layer* aLayerContext,
+ const nsIntRegion& aRegion, PushedGroup& aGroupResult);
+
+ void PopGroupForLayer(PushedGroup& aGroup);
+
+ virtual bool IsCompositingCheap() override { return false; }
+ virtual int32_t GetMaxTextureSize() const override { return INT32_MAX; }
+ bool CompositorMightResample() { return mCompositorMightResample; }
+
+ TimeStamp GetCompositionTime() const { return mCompositionTime; }
+
+ protected:
+ enum TransactionPhase {
+ PHASE_NONE,
+ PHASE_CONSTRUCTION,
+ PHASE_DRAWING,
+ PHASE_FORWARD
+ };
+ TransactionPhase mPhase;
+
+ // This is the main body of the PaintLayer routine which will if it has
+ // children, recurse into PaintLayer() otherwise it will paint using the
+ // underlying Paint() method of the Layer. It will not do both.
+ void PaintSelfOrChildren(PaintLayerContext& aPaintContext,
+ gfxContext* aGroupTarget);
+
+ // Paint the group onto the underlying target. This is used by PaintLayer to
+ // flush the group to the underlying target.
+ void FlushGroup(PaintLayerContext& aPaintContext,
+ bool aNeedsClipToVisibleRegion);
+
+ // Paints aLayer to mTarget.
+ void PaintLayer(gfxContext* aTarget, Layer* aLayer,
+ DrawPaintedLayerCallback aCallback, void* aCallbackData);
+
+ // Clear the contents of a layer
+ void ClearLayer(Layer* aLayer);
+
+ bool EndTransactionInternal(DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ EndTransactionFlags aFlags = END_DEFAULT);
+
+ void FlashWidgetUpdateArea(gfxContext* aContext);
+
+ void SetCompositionTime(TimeStamp aTimeStamp) {
+ mCompositionTime = aTimeStamp;
+ }
+
+ // Widget whose surface should be used as the basis for PaintedLayer
+ // buffers.
+ nsIWidget* mWidget;
+ // The default context for BeginTransaction.
+ RefPtr<gfxContext> mDefaultTarget;
+ // The context to draw into.
+ RefPtr<gfxContext> mTarget;
+ // Image factory we use.
+ RefPtr<ImageFactory> mFactory;
+
+ BufferMode mDoubleBuffering;
+ BasicLayerManagerType mType;
+ bool mUsingDefaultTarget;
+ bool mTransactionIncomplete;
+ bool mCompositorMightResample;
+
+ TimeStamp mCompositionTime;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_BASICLAYERS_H */
diff --git a/gfx/layers/basic/BasicLayersImpl.cpp b/gfx/layers/basic/BasicLayersImpl.cpp
new file mode 100644
index 0000000000..80c5df5f51
--- /dev/null
+++ b/gfx/layers/basic/BasicLayersImpl.cpp
@@ -0,0 +1,219 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "BasicLayersImpl.h"
+#include <new> // for operator new
+#include "Layers.h" // for Layer, etc
+#include "basic/BasicImplData.h" // for BasicImplData
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/DebugOnly.h" // for DebugOnly
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "AutoMaskData.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+bool GetMaskData(Layer* aMaskLayer, const Point& aDeviceOffset,
+ AutoMoz2DMaskData* aMaskData) {
+ if (aMaskLayer) {
+ RefPtr<SourceSurface> surface =
+ static_cast<BasicImplData*>(aMaskLayer->ImplData())
+ ->GetAsSourceSurface();
+ if (surface) {
+ Matrix transform;
+ Matrix4x4 effectiveTransform = aMaskLayer->GetEffectiveTransform();
+ DebugOnly<bool> maskIs2D = effectiveTransform.CanDraw2D(&transform);
+ NS_ASSERTION(maskIs2D, "How did we end up with a 3D transform here?!");
+ transform.PostTranslate(-aDeviceOffset.x, -aDeviceOffset.y);
+ aMaskData->Construct(transform, surface);
+ return true;
+ }
+ }
+ return false;
+}
+
+already_AddRefed<SourceSurface> GetMaskForLayer(Layer* aLayer,
+ Matrix* aMaskTransform) {
+ if (!aLayer->GetMaskLayer()) {
+ return nullptr;
+ }
+
+ MOZ_ASSERT(aMaskTransform);
+
+ AutoMoz2DMaskData mask;
+ if (GetMaskData(aLayer->GetMaskLayer(), Point(), &mask)) {
+ *aMaskTransform = mask.GetTransform();
+ RefPtr<SourceSurface> surf = mask.GetSurface();
+ return surf.forget();
+ }
+
+ return nullptr;
+}
+
+void PaintWithMask(gfxContext* aContext, float aOpacity, Layer* aMaskLayer) {
+ AutoMoz2DMaskData mask;
+ if (GetMaskData(aMaskLayer, Point(), &mask)) {
+ aContext->SetMatrix(mask.GetTransform());
+ aContext->Mask(mask.GetSurface(), aOpacity);
+ return;
+ }
+
+ // if there is no mask, just paint normally
+ aContext->Paint(aOpacity);
+}
+
+void FillRectWithMask(DrawTarget* aDT, const Rect& aRect,
+ const DeviceColor& aColor, const DrawOptions& aOptions,
+ SourceSurface* aMaskSource,
+ const Matrix* aMaskTransform) {
+ if (aMaskSource && aMaskTransform) {
+ aDT->PushClipRect(aRect);
+ Matrix oldTransform = aDT->GetTransform();
+
+ aDT->SetTransform(*aMaskTransform);
+ aDT->MaskSurface(ColorPattern(aColor), aMaskSource, Point(), aOptions);
+ aDT->SetTransform(oldTransform);
+ aDT->PopClip();
+ return;
+ }
+
+ aDT->FillRect(aRect, ColorPattern(aColor), aOptions);
+}
+void FillRectWithMask(DrawTarget* aDT, const gfx::Point& aDeviceOffset,
+ const Rect& aRect, const DeviceColor& aColor,
+ const DrawOptions& aOptions, Layer* aMaskLayer) {
+ AutoMoz2DMaskData mask;
+ if (GetMaskData(aMaskLayer, aDeviceOffset, &mask)) {
+ const Matrix& maskTransform = mask.GetTransform();
+ FillRectWithMask(aDT, aRect, aColor, aOptions, mask.GetSurface(),
+ &maskTransform);
+ return;
+ }
+
+ FillRectWithMask(aDT, aRect, aColor, aOptions);
+}
+
+void FillRectWithMask(DrawTarget* aDT, const Rect& aRect,
+ SourceSurface* aSurface, SamplingFilter aSamplingFilter,
+ const DrawOptions& aOptions, ExtendMode aExtendMode,
+ SourceSurface* aMaskSource, const Matrix* aMaskTransform,
+ const Matrix* aSurfaceTransform) {
+ if (aMaskSource && aMaskTransform) {
+ aDT->PushClipRect(aRect);
+ Matrix oldTransform = aDT->GetTransform();
+
+ Matrix inverseMask = *aMaskTransform;
+ inverseMask.Invert();
+
+ Matrix transform = oldTransform * inverseMask;
+ if (aSurfaceTransform) {
+ transform = (*aSurfaceTransform) * transform;
+ }
+
+ SurfacePattern source(aSurface, aExtendMode, transform, aSamplingFilter);
+
+ aDT->SetTransform(*aMaskTransform);
+ aDT->MaskSurface(source, aMaskSource, Point(0, 0), aOptions);
+
+ aDT->SetTransform(oldTransform);
+ aDT->PopClip();
+ return;
+ }
+
+ aDT->FillRect(
+ aRect,
+ SurfacePattern(aSurface, aExtendMode,
+ aSurfaceTransform ? (*aSurfaceTransform) : Matrix(),
+ aSamplingFilter),
+ aOptions);
+}
+
+void FillRectWithMask(DrawTarget* aDT, const gfx::Point& aDeviceOffset,
+ const Rect& aRect, SourceSurface* aSurface,
+ SamplingFilter aSamplingFilter,
+ const DrawOptions& aOptions, Layer* aMaskLayer) {
+ AutoMoz2DMaskData mask;
+ if (GetMaskData(aMaskLayer, aDeviceOffset, &mask)) {
+ const Matrix& maskTransform = mask.GetTransform();
+ FillRectWithMask(aDT, aRect, aSurface, aSamplingFilter, aOptions,
+ ExtendMode::CLAMP, mask.GetSurface(), &maskTransform);
+ return;
+ }
+
+ FillRectWithMask(aDT, aRect, aSurface, aSamplingFilter, aOptions,
+ ExtendMode::CLAMP);
+}
+
+void FillPathWithMask(DrawTarget* aDT, const Path* aPath, const Rect& aClipRect,
+ const DeviceColor& aColor, const DrawOptions& aOptions,
+ SourceSurface* aMaskSource,
+ const Matrix* aMaskTransform) {
+ if (aMaskSource && aMaskTransform) {
+ aDT->PushClipRect(aClipRect);
+ Matrix oldTransform = aDT->GetTransform();
+
+ aDT->SetTransform(*aMaskTransform);
+ aDT->MaskSurface(ColorPattern(aColor), aMaskSource, Point(), aOptions);
+ aDT->SetTransform(oldTransform);
+ aDT->PopClip();
+ return;
+ }
+
+ aDT->Fill(aPath, ColorPattern(aColor), aOptions);
+}
+
+void FillPathWithMask(DrawTarget* aDT, const Path* aPath, const Rect& aClipRect,
+ SourceSurface* aSurface, SamplingFilter aSamplingFilter,
+ const DrawOptions& aOptions, ExtendMode aExtendMode,
+ SourceSurface* aMaskSource, const Matrix* aMaskTransform,
+ const Matrix* aSurfaceTransform) {
+ if (aMaskSource && aMaskTransform) {
+ aDT->PushClipRect(aClipRect);
+ Matrix oldTransform = aDT->GetTransform();
+
+ Matrix inverseMask = *aMaskTransform;
+ inverseMask.Invert();
+
+ Matrix transform = oldTransform * inverseMask;
+ if (aSurfaceTransform) {
+ transform = (*aSurfaceTransform) * transform;
+ }
+
+ SurfacePattern source(aSurface, aExtendMode, transform, aSamplingFilter);
+
+ aDT->SetTransform(*aMaskTransform);
+ aDT->MaskSurface(source, aMaskSource, Point(0, 0), aOptions);
+ aDT->SetTransform(oldTransform);
+ aDT->PopClip();
+ return;
+ }
+
+ aDT->Fill(aPath,
+ SurfacePattern(aSurface, aExtendMode,
+ aSurfaceTransform ? (*aSurfaceTransform) : Matrix(),
+ aSamplingFilter),
+ aOptions);
+}
+
+BasicImplData* ToData(Layer* aLayer) {
+ return static_cast<BasicImplData*>(aLayer->ImplData());
+}
+
+gfx::CompositionOp GetEffectiveOperator(Layer* aLayer) {
+ CompositionOp op = aLayer->GetEffectiveMixBlendMode();
+
+ if (op != CompositionOp::OP_OVER) {
+ return op;
+ }
+
+ return ToData(aLayer)->GetOperator();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/basic/BasicLayersImpl.h b/gfx/layers/basic/BasicLayersImpl.h
new file mode 100644
index 0000000000..37d968ca80
--- /dev/null
+++ b/gfx/layers/basic/BasicLayersImpl.h
@@ -0,0 +1,145 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef GFX_BASICLAYERSIMPL_H
+#define GFX_BASICLAYERSIMPL_H
+
+#include "BasicImplData.h" // for BasicImplData
+#include "BasicLayers.h" // for BasicLayerManager
+#include "ReadbackLayer.h" // for ReadbackLayer
+#include "gfxContext.h" // for gfxContext, etc
+#include "mozilla/Attributes.h" // for MOZ_STACK_CLASS
+#include "mozilla/Maybe.h" // for Maybe
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for gfxContext::Release, etc
+#include "nsRegion.h" // for nsIntRegion
+
+namespace mozilla {
+namespace gfx {
+class DrawTarget;
+} // namespace gfx
+
+namespace layers {
+
+class AutoMoz2DMaskData;
+class Layer;
+
+class AutoSetOperator {
+ typedef mozilla::gfx::CompositionOp CompositionOp;
+
+ public:
+ AutoSetOperator(gfxContext* aContext, CompositionOp aOperator) {
+ if (aOperator != CompositionOp::OP_OVER) {
+ aContext->SetOp(aOperator);
+ mContext = aContext;
+ }
+ }
+ ~AutoSetOperator() {
+ if (mContext) {
+ mContext->SetOp(CompositionOp::OP_OVER);
+ }
+ }
+
+ private:
+ RefPtr<gfxContext> mContext;
+};
+
+class BasicReadbackLayer : public ReadbackLayer, public BasicImplData {
+ public:
+ explicit BasicReadbackLayer(BasicLayerManager* aLayerManager)
+ : ReadbackLayer(aLayerManager, static_cast<BasicImplData*>(this)) {
+ MOZ_COUNT_CTOR(BasicReadbackLayer);
+ }
+
+ protected:
+ MOZ_COUNTED_DTOR_OVERRIDE(BasicReadbackLayer)
+
+ public:
+ void SetVisibleRegion(const LayerIntRegion& aRegion) override {
+ NS_ASSERTION(BasicManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ ReadbackLayer::SetVisibleRegion(aRegion);
+ }
+
+ protected:
+ BasicLayerManager* BasicManager() {
+ return static_cast<BasicLayerManager*>(mManager);
+ }
+};
+
+/*
+ * Extract a mask surface for a mask layer
+ * Returns true and through outparams a surface for the mask layer if
+ * a mask layer is present and has a valid surface and transform;
+ * false otherwise.
+ * The transform for the layer will be put in aMaskData
+ */
+bool GetMaskData(Layer* aMaskLayer, const gfx::Point& aDeviceOffset,
+ AutoMoz2DMaskData* aMaskData);
+
+already_AddRefed<gfx::SourceSurface> GetMaskForLayer(
+ Layer* aLayer, gfx::Matrix* aMaskTransform);
+
+// Paint the current source to a context using a mask, if present
+void PaintWithMask(gfxContext* aContext, float aOpacity, Layer* aMaskLayer);
+
+// Fill the rect with the source, using a mask and opacity, if present
+void FillRectWithMask(gfx::DrawTarget* aDT, const gfx::Rect& aRect,
+ const gfx::DeviceColor& aColor,
+ const gfx::DrawOptions& aOptions,
+ gfx::SourceSurface* aMaskSource = nullptr,
+ const gfx::Matrix* aMaskTransform = nullptr);
+void FillRectWithMask(gfx::DrawTarget* aDT, const gfx::Rect& aRect,
+ gfx::SourceSurface* aSurface,
+ gfx::SamplingFilter aSamplingFilter,
+ const gfx::DrawOptions& aOptions,
+ gfx::ExtendMode aExtendMode,
+ gfx::SourceSurface* aMaskSource = nullptr,
+ const gfx::Matrix* aMaskTransform = nullptr,
+ const gfx::Matrix* aSurfaceTransform = nullptr);
+void FillRectWithMask(gfx::DrawTarget* aDT, const gfx::Point& aDeviceOffset,
+ const gfx::Rect& aRect, gfx::SourceSurface* aSurface,
+ gfx::SamplingFilter aSamplingFilter,
+ const gfx::DrawOptions& aOptions, Layer* aMaskLayer);
+void FillRectWithMask(gfx::DrawTarget* aDT, const gfx::Point& aDeviceOffset,
+ const gfx::Rect& aRect, const gfx::DeviceColor& aColor,
+ const gfx::DrawOptions& aOptions, Layer* aMaskLayer);
+
+void FillPathWithMask(gfx::DrawTarget* aDT, const gfx::Path* aPath,
+ const gfx::Rect& aClipRect,
+ const gfx::DeviceColor& aColor,
+ const gfx::DrawOptions& aOptions,
+ gfx::SourceSurface* aMaskSource = nullptr,
+ const gfx::Matrix* aMaskTransform = nullptr);
+void FillPathWithMask(gfx::DrawTarget* aDT, const gfx::Path* aPath,
+ const gfx::Rect& aClipRect, gfx::SourceSurface* aSurface,
+ gfx::SamplingFilter aSamplingFilter,
+ const gfx::DrawOptions& aOptions,
+ gfx::ExtendMode aExtendMode,
+ gfx::SourceSurface* aMaskSource,
+ const gfx::Matrix* aMaskTransform,
+ const gfx::Matrix* aSurfaceTransform);
+
+BasicImplData* ToData(Layer* aLayer);
+
+/**
+ * Returns the operator to be used when blending and compositing this layer.
+ * Currently there is no way to specify both a blending and a compositing
+ * operator other than normal and source over respectively.
+ *
+ * If the layer has
+ * an effective blend mode operator other than normal, as returned by
+ * GetEffectiveMixBlendMode, this operator is used for blending, and source
+ * over is used for compositing.
+ * If the blend mode for this layer is normal, the compositing operator
+ * returned by GetOperator is used.
+ */
+gfx::CompositionOp GetEffectiveOperator(Layer* aLayer);
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/basic/BasicPaintedLayer.cpp b/gfx/layers/basic/BasicPaintedLayer.cpp
new file mode 100644
index 0000000000..c8c9750831
--- /dev/null
+++ b/gfx/layers/basic/BasicPaintedLayer.cpp
@@ -0,0 +1,239 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "BasicPaintedLayer.h"
+#include <stdint.h> // for uint32_t
+#include "GeckoProfiler.h" // for AUTO_PROFILER_LABEL
+#include "ReadbackLayer.h" // for ReadbackLayer, ReadbackSink
+#include "ReadbackProcessor.h" // for ReadbackProcessor::Update, etc
+#include "RenderTrace.h" // for RenderTraceInvalidateEnd, etc
+#include "BasicLayersImpl.h" // for AutoMaskData, etc
+#include "gfxContext.h" // for gfxContext, etc
+#include "gfxRect.h" // for gfxRect
+#include "gfxUtils.h" // for gfxUtils
+#include "mozilla/gfx/2D.h" // for DrawTarget
+#include "mozilla/gfx/BaseRect.h" // for BaseRect
+#include "mozilla/gfx/Matrix.h" // for Matrix
+#include "mozilla/gfx/Rect.h" // for Rect, IntRect
+#include "mozilla/gfx/Types.h" // for Float, etc
+#include "mozilla/layers/LayersTypes.h"
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsISupportsImpl.h" // for gfxContext::Release, etc
+#include "nsPoint.h" // for nsIntPoint
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsTArray.h" // for nsTArray, nsTArray_Impl
+#include "AutoMaskData.h"
+#include "gfx2DGlue.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+static nsIntRegion IntersectWithClip(const nsIntRegion& aRegion,
+ gfxContext* aContext) {
+ gfxRect clip = aContext->GetClipExtents();
+ nsIntRegion result;
+ result.And(aRegion, IntRect::RoundOut(clip.X(), clip.Y(), clip.Width(),
+ clip.Height()));
+ return result;
+}
+
+void BasicPaintedLayer::PaintThebes(
+ gfxContext* aContext, Layer* aMaskLayer,
+ LayerManager::DrawPaintedLayerCallback aCallback, void* aCallbackData) {
+ AUTO_PROFILER_LABEL("BasicPaintedLayer::PaintThebes", GRAPHICS);
+
+ NS_ASSERTION(BasicManager()->InDrawing(), "Can only draw in drawing phase");
+
+ float opacity = GetEffectiveOpacity();
+ CompositionOp effectiveOperator = GetEffectiveOperator(this);
+
+ if (!BasicManager()->IsRetained()) {
+ ClearValidRegion();
+ mContentClient->Clear();
+
+ nsIntRegion toDraw =
+ IntersectWithClip(GetLocalVisibleRegion().ToUnknownRegion(), aContext);
+
+ RenderTraceInvalidateStart(this, "FFFF00", toDraw.GetBounds());
+
+ if (!toDraw.IsEmpty() && !IsHidden()) {
+ if (!aCallback) {
+ BasicManager()->SetTransactionIncomplete();
+ return;
+ }
+
+ aContext->Save();
+
+ bool needsGroup = opacity != 1.0 ||
+ effectiveOperator != CompositionOp::OP_OVER ||
+ aMaskLayer;
+ RefPtr<gfxContext> context = nullptr;
+ BasicLayerManager::PushedGroup group;
+ bool availableGroup = false;
+
+ if (needsGroup) {
+ availableGroup =
+ BasicManager()->PushGroupForLayer(aContext, this, toDraw, group);
+ if (availableGroup) {
+ context = group.mGroupTarget;
+ }
+ } else {
+ context = aContext;
+ }
+ if (context) {
+ DrawTarget* target = context->GetDrawTarget();
+ bool oldAA = target->GetPermitSubpixelAA();
+ SetAntialiasingFlags(this, target);
+ aCallback(this, context, toDraw, toDraw, DrawRegionClip::NONE,
+ nsIntRegion(), aCallbackData);
+ target->SetPermitSubpixelAA(oldAA);
+ }
+ if (needsGroup && availableGroup) {
+ BasicManager()->PopGroupForLayer(group);
+ }
+
+ aContext->Restore();
+ }
+
+ RenderTraceInvalidateEnd(this, "FFFF00");
+ return;
+ }
+
+ if (BasicManager()->IsTransactionIncomplete()) return;
+
+ gfxRect clipExtents;
+ clipExtents = aContext->GetClipExtents();
+
+ // Pull out the mask surface and transform here, because the mask
+ // is internal to basic layers
+ AutoMoz2DMaskData mask;
+ SourceSurface* maskSurface = nullptr;
+ Matrix maskTransform;
+ if (GetMaskData(aMaskLayer, aContext->GetDeviceOffset(), &mask)) {
+ maskSurface = mask.GetSurface();
+ maskTransform = mask.GetTransform();
+ }
+
+ if (!IsHidden() && !clipExtents.IsEmpty()) {
+ mContentClient->DrawTo(this, aContext->GetDrawTarget(), opacity,
+ effectiveOperator, maskSurface, &maskTransform);
+ }
+}
+
+void BasicPaintedLayer::Validate(
+ LayerManager::DrawPaintedLayerCallback aCallback, void* aCallbackData,
+ ReadbackProcessor* aReadback) {
+ if (!mContentClient) {
+ // This client will have a null Forwarder, which means it will not have
+ // a ContentHost on the other side.
+ mContentClient = new ContentClientBasic(mBackend);
+ }
+
+ if (!BasicManager()->IsRetained()) {
+ return;
+ }
+
+ nsTArray<ReadbackProcessor::Update> readbackUpdates;
+ if (aReadback && UsedForReadback()) {
+ aReadback->GetPaintedLayerUpdates(this, &readbackUpdates);
+ }
+
+ uint32_t flags = 0;
+#ifndef MOZ_WIDGET_ANDROID
+ if (BasicManager()->CompositorMightResample()) {
+ flags |= ContentClient::PAINT_WILL_RESAMPLE;
+ }
+ if (!(flags & ContentClient::PAINT_WILL_RESAMPLE)) {
+ if (MayResample()) {
+ flags |= ContentClient::PAINT_WILL_RESAMPLE;
+ }
+ }
+#endif
+ if (mDrawAtomically) {
+ flags |= ContentClient::PAINT_NO_ROTATION;
+ }
+ PaintState state = mContentClient->BeginPaint(this, flags);
+ SubtractFromValidRegion(state.mRegionToInvalidate);
+
+ DrawTarget* target = mContentClient->BorrowDrawTargetForPainting(state);
+ if (target && target->IsValid()) {
+ // The area that became invalid and is visible needs to be repainted
+ // (this could be the whole visible area if our buffer switched
+ // from RGB to RGBA, because we might need to repaint with
+ // subpixel AA)
+ state.mRegionToInvalidate.And(state.mRegionToInvalidate,
+ GetLocalVisibleRegion().ToUnknownRegion());
+ SetAntialiasingFlags(this, target);
+
+ RenderTraceInvalidateStart(this, "FFFF00", state.mRegionToDraw.GetBounds());
+
+ RefPtr<gfxContext> ctx =
+ gfxContext::CreatePreservingTransformOrNull(target);
+ MOZ_ASSERT(ctx); // already checked the target above
+
+ PaintBuffer(ctx, state.mRegionToDraw, state.mRegionToDraw,
+ state.mRegionToInvalidate, state.mClip, aCallback,
+ aCallbackData);
+ MOZ_LAYERS_LOG_IF_SHADOWABLE(this,
+ ("Layer::Mutated(%p) PaintThebes", this));
+ Mutated();
+ ctx = nullptr;
+ mContentClient->ReturnDrawTarget(target);
+ target = nullptr;
+
+ RenderTraceInvalidateEnd(this, "FFFF00");
+ } else {
+ if (target) {
+ mContentClient->ReturnDrawTarget(target);
+ target = nullptr;
+ }
+
+ // It's possible that state.mRegionToInvalidate is nonempty here,
+ // if we are shrinking the valid region to nothing. So use mRegionToDraw
+ // instead.
+ NS_WARNING_ASSERTION(
+ state.mRegionToDraw.IsEmpty(),
+ "No context when we have something to draw, resource exhaustion?");
+ }
+
+ for (uint32_t i = 0; i < readbackUpdates.Length(); ++i) {
+ ReadbackProcessor::Update& update = readbackUpdates[i];
+ nsIntPoint offset = update.mLayer->GetBackgroundLayerOffset();
+ RefPtr<DrawTarget> dt = update.mLayer->GetSink()->BeginUpdate(
+ update.mUpdateRect + offset, update.mSequenceCounter);
+ if (dt) {
+ NS_ASSERTION(GetEffectiveOpacity() == 1.0,
+ "Should only read back opaque layers");
+ NS_ASSERTION(!GetMaskLayer(),
+ "Should only read back layers without masks");
+ dt->SetTransform(dt->GetTransform().PreTranslate(offset.x, offset.y));
+ mContentClient->DrawTo(this, dt, 1.0, CompositionOp::OP_OVER, nullptr,
+ nullptr);
+ update.mLayer->GetSink()->EndUpdate(update.mUpdateRect + offset);
+ }
+ }
+}
+
+already_AddRefed<PaintedLayer> BasicLayerManager::CreatePaintedLayer() {
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+
+ BackendType backend = gfxPlatform::GetPlatform()->GetDefaultContentBackend();
+
+ if (mDefaultTarget) {
+ backend = mDefaultTarget->GetDrawTarget()->GetBackendType();
+ } else if (mType == BLM_WIDGET) {
+ backend = gfxPlatform::GetPlatform()->GetContentBackendFor(
+ LayersBackend::LAYERS_BASIC);
+ }
+
+ RefPtr<PaintedLayer> layer = new BasicPaintedLayer(this, backend);
+ return layer.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/basic/BasicPaintedLayer.h b/gfx/layers/basic/BasicPaintedLayer.h
new file mode 100644
index 0000000000..de6184f34c
--- /dev/null
+++ b/gfx/layers/basic/BasicPaintedLayer.h
@@ -0,0 +1,121 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef GFX_BASICPAINTEDLAYER_H
+#define GFX_BASICPAINTEDLAYER_H
+
+#include "Layers.h" // for PaintedLayer, LayerManager, etc
+#include "RotatedBuffer.h" // for RotatedBuffer, etc
+#include "BasicImplData.h" // for BasicImplData
+#include "BasicLayers.h" // for BasicLayerManager
+#include "gfxPoint.h" // for gfxPoint
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/gfx/BasePoint.h" // for BasePoint
+#include "mozilla/layers/ContentClient.h" // for ContentClientBasic
+#include "mozilla/mozalloc.h" // for operator delete
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsRegion.h" // for nsIntRegion
+class gfxContext;
+
+namespace mozilla {
+namespace layers {
+
+class ReadbackProcessor;
+
+class BasicPaintedLayer : public PaintedLayer, public BasicImplData {
+ public:
+ typedef ContentClient::PaintState PaintState;
+ typedef ContentClient::ContentType ContentType;
+
+ BasicPaintedLayer(BasicLayerManager* aLayerManager, gfx::BackendType aBackend)
+ : PaintedLayer(aLayerManager, static_cast<BasicImplData*>(this)),
+ mContentClient(nullptr),
+ mBackend(aBackend) {
+ MOZ_COUNT_CTOR(BasicPaintedLayer);
+ }
+
+ protected:
+ MOZ_COUNTED_DTOR_OVERRIDE(BasicPaintedLayer)
+
+ public:
+ void SetVisibleRegion(const LayerIntRegion& aRegion) override {
+ NS_ASSERTION(BasicManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ PaintedLayer::SetVisibleRegion(aRegion);
+ }
+ void InvalidateRegion(const nsIntRegion& aRegion) override {
+ NS_ASSERTION(BasicManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ mInvalidRegion.Add(aRegion);
+ UpdateValidRegionAfterInvalidRegionChanged();
+ }
+
+ void PaintThebes(gfxContext* aContext, Layer* aMaskLayer,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData) override;
+
+ void Validate(LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData, ReadbackProcessor* aReadback) override;
+
+ void ClearCachedResources() override {
+ if (mContentClient) {
+ mContentClient->Clear();
+ }
+ ClearValidRegion();
+ }
+
+ void ComputeEffectiveTransforms(
+ const gfx::Matrix4x4& aTransformToSurface) override {
+ if (!BasicManager()->IsRetained()) {
+ // Don't do any snapping of our transform, since we're just going to
+ // draw straight through without intermediate buffers.
+ mEffectiveTransform = GetLocalTransform() * aTransformToSurface;
+ if (gfxPoint(0, 0) != mResidualTranslation) {
+ mResidualTranslation = gfxPoint(0, 0);
+ ClearValidRegion();
+ }
+ ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
+ return;
+ }
+ PaintedLayer::ComputeEffectiveTransforms(aTransformToSurface);
+ }
+
+ BasicLayerManager* BasicManager() {
+ return static_cast<BasicLayerManager*>(mManager);
+ }
+
+ protected:
+ virtual void PaintBuffer(gfxContext* aContext,
+ const nsIntRegion& aRegionToDraw,
+ const nsIntRegion& aExtendedRegionToDraw,
+ const nsIntRegion& aRegionToInvalidate,
+ DrawRegionClip aClip,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData) {
+ if (!aCallback) {
+ BasicManager()->SetTransactionIncomplete();
+ return;
+ }
+ aCallback(this, aContext, aExtendedRegionToDraw, aExtendedRegionToDraw,
+ aClip, aRegionToInvalidate, aCallbackData);
+ // Everything that's visible has been validated. Do this instead of just
+ // OR-ing with aRegionToDraw, since that can lead to a very complex region
+ // here (OR doesn't automatically simplify to the simplest possible
+ // representation of a region.)
+ nsIntRegion tmp;
+ tmp.Or(mVisibleRegion.ToUnknownRegion(), aExtendedRegionToDraw);
+ AddToValidRegion(tmp);
+ }
+
+ RefPtr<ContentClientBasic> mContentClient;
+ gfx::BackendType mBackend;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/basic/MacIOSurfaceTextureHostBasic.cpp b/gfx/layers/basic/MacIOSurfaceTextureHostBasic.cpp
new file mode 100644
index 0000000000..1de9ea8246
--- /dev/null
+++ b/gfx/layers/basic/MacIOSurfaceTextureHostBasic.cpp
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "MacIOSurfaceTextureHostBasic.h"
+#include "mozilla/gfx/MacIOSurface.h"
+#include "MacIOSurfaceHelpers.h"
+
+namespace mozilla {
+namespace layers {
+
+MacIOSurfaceTextureSourceBasic::MacIOSurfaceTextureSourceBasic(
+ MacIOSurface* aSurface)
+ : mSurface(aSurface) {
+ MOZ_COUNT_CTOR(MacIOSurfaceTextureSourceBasic);
+}
+
+MacIOSurfaceTextureSourceBasic::~MacIOSurfaceTextureSourceBasic() {
+ MOZ_COUNT_DTOR(MacIOSurfaceTextureSourceBasic);
+}
+
+gfx::IntSize MacIOSurfaceTextureSourceBasic::GetSize() const {
+ return gfx::IntSize(mSurface->GetDevicePixelWidth(),
+ mSurface->GetDevicePixelHeight());
+}
+
+gfx::SurfaceFormat MacIOSurfaceTextureSourceBasic::GetFormat() const {
+ // Set the format the same way as CreateSourceSurfaceFromMacIOSurface.
+ return mSurface->GetFormat() == gfx::SurfaceFormat::NV12
+ ? gfx::SurfaceFormat::B8G8R8X8
+ : gfx::SurfaceFormat::B8G8R8A8;
+}
+
+MacIOSurfaceTextureHostBasic::MacIOSurfaceTextureHostBasic(
+ TextureFlags aFlags, const SurfaceDescriptorMacIOSurface& aDescriptor)
+ : TextureHost(aFlags) {
+ mSurface = MacIOSurface::LookupSurface(
+ aDescriptor.surfaceId(), aDescriptor.scaleFactor(),
+ !aDescriptor.isOpaque(), aDescriptor.yUVColorSpace());
+}
+
+gfx::SourceSurface* MacIOSurfaceTextureSourceBasic::GetSurface(
+ gfx::DrawTarget* aTarget) {
+ if (!mSourceSurface) {
+ mSourceSurface = CreateSourceSurfaceFromMacIOSurface(mSurface);
+ }
+ return mSourceSurface;
+}
+
+bool MacIOSurfaceTextureHostBasic::Lock() {
+ if (!mProvider) {
+ return false;
+ }
+
+ if (!mTextureSource) {
+ mTextureSource = new MacIOSurfaceTextureSourceBasic(mSurface);
+ }
+ return true;
+}
+
+void MacIOSurfaceTextureHostBasic::SetTextureSourceProvider(
+ TextureSourceProvider* aProvider) {
+ if (!aProvider->AsCompositor() ||
+ !aProvider->AsCompositor()->AsBasicCompositor()) {
+ mTextureSource = nullptr;
+ return;
+ }
+
+ mProvider = aProvider;
+
+ if (mTextureSource) {
+ mTextureSource->SetTextureSourceProvider(aProvider);
+ }
+}
+
+gfx::SurfaceFormat MacIOSurfaceTextureHostBasic::GetFormat() const {
+ return mSurface->HasAlpha() ? gfx::SurfaceFormat::R8G8B8A8
+ : gfx::SurfaceFormat::B8G8R8X8;
+}
+
+gfx::IntSize MacIOSurfaceTextureHostBasic::GetSize() const {
+ return gfx::IntSize(mSurface->GetDevicePixelWidth(),
+ mSurface->GetDevicePixelHeight());
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/basic/MacIOSurfaceTextureHostBasic.h b/gfx/layers/basic/MacIOSurfaceTextureHostBasic.h
new file mode 100644
index 0000000000..9dd4c38a26
--- /dev/null
+++ b/gfx/layers/basic/MacIOSurfaceTextureHostBasic.h
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef MOZILLA_GFX_MACIOSURFACETEXTUREHOST_BASIC_H
+#define MOZILLA_GFX_MACIOSURFACETEXTUREHOST_BASIC_H
+
+#include "mozilla/layers/BasicCompositor.h"
+#include "mozilla/layers/TextureHostBasic.h"
+
+class MacIOSurface;
+
+namespace mozilla {
+namespace layers {
+
+class BasicCompositor;
+
+/**
+ * A texture source meant for use with BasicCompositor.
+ *
+ * It does not own any GL texture, and attaches its shared handle to one of
+ * the compositor's temporary textures when binding.
+ */
+class MacIOSurfaceTextureSourceBasic : public TextureSourceBasic,
+ public TextureSource {
+ public:
+ explicit MacIOSurfaceTextureSourceBasic(MacIOSurface* aSurface);
+ virtual ~MacIOSurfaceTextureSourceBasic();
+
+ const char* Name() const override { return "MacIOSurfaceTextureSourceBasic"; }
+
+ TextureSourceBasic* AsSourceBasic() override { return this; }
+
+ gfx::IntSize GetSize() const override;
+ gfx::SurfaceFormat GetFormat() const override;
+ gfx::SourceSurface* GetSurface(gfx::DrawTarget* aTarget) override;
+
+ void DeallocateDeviceData() override {}
+
+ protected:
+ RefPtr<MacIOSurface> mSurface;
+ RefPtr<gfx::SourceSurface> mSourceSurface;
+};
+
+/**
+ * A TextureHost for shared MacIOSurface
+ *
+ * Most of the logic actually happens in MacIOSurfaceTextureSourceBasic.
+ */
+class MacIOSurfaceTextureHostBasic : public TextureHost {
+ public:
+ MacIOSurfaceTextureHostBasic(
+ TextureFlags aFlags, const SurfaceDescriptorMacIOSurface& aDescriptor);
+
+ void SetTextureSourceProvider(TextureSourceProvider* aProvider) override;
+
+ bool Lock() override;
+
+ gfx::SurfaceFormat GetFormat() const override;
+
+ bool BindTextureSource(CompositableTextureSourceRef& aTexture) override {
+ aTexture = mTextureSource;
+ return !!aTexture;
+ }
+
+ already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override {
+ return nullptr; // XXX - implement this (for MOZ_DUMP_PAINTING)
+ }
+
+ gfx::IntSize GetSize() const override;
+ MacIOSurface* GetMacIOSurface() override { return mSurface; }
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ const char* Name() override { return "MacIOSurfaceTextureHostBasic"; }
+#endif
+
+ protected:
+ RefPtr<MacIOSurfaceTextureSourceBasic> mTextureSource;
+ RefPtr<MacIOSurface> mSurface;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_MACIOSURFACETEXTUREHOSTOGL_BASIC_H
diff --git a/gfx/layers/basic/TextureClientX11.cpp b/gfx/layers/basic/TextureClientX11.cpp
new file mode 100644
index 0000000000..06f41e8d0b
--- /dev/null
+++ b/gfx/layers/basic/TextureClientX11.cpp
@@ -0,0 +1,140 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "mozilla/layers/TextureClientX11.h"
+
+#include "mozilla/layers/CompositableClient.h"
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/layers/ShadowLayerUtilsX11.h"
+#include "mozilla/layers/TextureForwarder.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Logging.h"
+#include "gfx2DGlue.h"
+#include "gfxPlatform.h"
+#include "gfxXlibSurface.h"
+
+#include "mozilla/X11Util.h"
+#include <X11/Xlib.h>
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+namespace mozilla::layers {
+
+X11TextureData::X11TextureData(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ bool aClientDeallocation, bool aIsCrossProcess,
+ gfxXlibSurface* aSurface)
+ : mSize(aSize),
+ mFormat(aFormat),
+ mSurface(aSurface),
+ mClientDeallocation(aClientDeallocation),
+ mIsCrossProcess(aIsCrossProcess) {
+ MOZ_ASSERT(mSurface);
+}
+
+bool X11TextureData::Lock(OpenMode aMode) { return true; }
+
+void X11TextureData::Unlock() {
+ if (mSurface && mIsCrossProcess) {
+ FinishX(DefaultXDisplay());
+ }
+}
+
+void X11TextureData::FillInfo(TextureData::Info& aInfo) const {
+ aInfo.size = mSize;
+ aInfo.format = mFormat;
+ aInfo.supportsMoz2D = true;
+ aInfo.hasIntermediateBuffer = false;
+ aInfo.hasSynchronization = false;
+ aInfo.canExposeMappedData = false;
+}
+
+bool X11TextureData::Serialize(SurfaceDescriptor& aOutDescriptor) {
+ MOZ_ASSERT(mSurface);
+ if (!mSurface) {
+ return false;
+ }
+
+ if (!mClientDeallocation) {
+ // Pass to the host the responsibility of freeing the pixmap. ReleasePixmap
+ // means the underlying pixmap will not be deallocated in mSurface's
+ // destructor. ToSurfaceDescriptor is at most called once per TextureClient.
+ mSurface->ReleasePixmap();
+ }
+
+ aOutDescriptor = SurfaceDescriptorX11(mSurface);
+ return true;
+}
+
+already_AddRefed<gfx::DrawTarget> X11TextureData::BorrowDrawTarget() {
+ MOZ_ASSERT(mSurface);
+ if (!mSurface) {
+ return nullptr;
+ }
+
+ IntSize size = mSurface->GetSize();
+ RefPtr<gfx::DrawTarget> dt =
+ Factory::CreateDrawTargetForCairoSurface(mSurface->CairoSurface(), size);
+
+ return dt.forget();
+}
+
+bool X11TextureData::UpdateFromSurface(gfx::SourceSurface* aSurface) {
+ RefPtr<DrawTarget> dt = BorrowDrawTarget();
+
+ if (!dt) {
+ return false;
+ }
+
+ dt->CopySurface(aSurface, IntRect(IntPoint(), aSurface->GetSize()),
+ IntPoint());
+
+ return true;
+}
+
+void X11TextureData::Deallocate(LayersIPCChannel*) { mSurface = nullptr; }
+
+TextureData* X11TextureData::CreateSimilar(
+ LayersIPCChannel* aAllocator, LayersBackend aLayersBackend,
+ TextureFlags aFlags, TextureAllocationFlags aAllocFlags) const {
+ return X11TextureData::Create(mSize, mFormat, aFlags, aAllocator);
+}
+
+X11TextureData* X11TextureData::Create(gfx::IntSize aSize,
+ gfx::SurfaceFormat aFormat,
+ TextureFlags aFlags,
+ LayersIPCChannel* aAllocator) {
+ MOZ_ASSERT(aSize.width >= 0 && aSize.height >= 0);
+ if (aSize.width <= 0 || aSize.height <= 0 ||
+ aSize.width > XLIB_IMAGE_SIDE_SIZE_LIMIT ||
+ aSize.height > XLIB_IMAGE_SIDE_SIZE_LIMIT) {
+ gfxDebug() << "Asking for X11 surface of invalid size " << aSize.width
+ << "x" << aSize.height;
+ return nullptr;
+ }
+ gfxImageFormat imageFormat = SurfaceFormatToImageFormat(aFormat);
+ RefPtr<gfxASurface> surface =
+ gfxPlatform::GetPlatform()->CreateOffscreenSurface(aSize, imageFormat);
+ if (!surface || surface->GetType() != gfxSurfaceType::Xlib) {
+ NS_ERROR("creating Xlib surface failed!");
+ return nullptr;
+ }
+
+ gfxXlibSurface* xlibSurface = static_cast<gfxXlibSurface*>(surface.get());
+
+ bool crossProcess = !aAllocator->IsSameProcess();
+ X11TextureData* texture = new X11TextureData(
+ aSize, aFormat, !!(aFlags & TextureFlags::DEALLOCATE_CLIENT),
+ crossProcess, xlibSurface);
+ if (crossProcess) {
+ FinishX(DefaultXDisplay());
+ }
+
+ return texture;
+}
+
+} // namespace mozilla::layers
diff --git a/gfx/layers/basic/TextureClientX11.h b/gfx/layers/basic/TextureClientX11.h
new file mode 100644
index 0000000000..2089157c42
--- /dev/null
+++ b/gfx/layers/basic/TextureClientX11.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef MOZILLA_GFX_TEXTURECLIENT_X11_H
+#define MOZILLA_GFX_TEXTURECLIENT_X11_H
+
+#include "mozilla/layers/TextureClient.h"
+#include "ISurfaceAllocator.h" // For IsSurfaceDescriptorValid
+
+namespace mozilla {
+namespace layers {
+
+class X11TextureData : public TextureData {
+ public:
+ static X11TextureData* Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ TextureFlags aFlags,
+ LayersIPCChannel* aAllocator);
+
+ virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
+
+ virtual bool Lock(OpenMode aMode) override;
+
+ virtual void Unlock() override;
+
+ virtual void FillInfo(TextureData::Info& aInfo) const override;
+
+ virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() override;
+
+ virtual void Deallocate(LayersIPCChannel*) override;
+
+ virtual TextureData* CreateSimilar(
+ LayersIPCChannel* aAllocator, LayersBackend aLayersBackend,
+ TextureFlags aFlags = TextureFlags::DEFAULT,
+ TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const override;
+
+ virtual bool UpdateFromSurface(gfx::SourceSurface* aSurface) override;
+
+ protected:
+ X11TextureData(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ bool aClientDeallocation, bool aIsCrossProcess,
+ gfxXlibSurface* aSurface);
+
+ gfx::IntSize mSize;
+ gfx::SurfaceFormat mFormat;
+ RefPtr<gfxXlibSurface> mSurface;
+ bool mClientDeallocation;
+ bool mIsCrossProcess;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/basic/TextureHostBasic.cpp b/gfx/layers/basic/TextureHostBasic.cpp
new file mode 100644
index 0000000000..c42fc2874c
--- /dev/null
+++ b/gfx/layers/basic/TextureHostBasic.cpp
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "TextureHostBasic.h"
+#ifdef XP_MACOSX
+# include "MacIOSurfaceTextureHostBasic.h"
+#endif
+
+using namespace mozilla::gl;
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace layers {
+
+already_AddRefed<TextureHost> CreateTextureHostBasic(
+ const SurfaceDescriptor& aDesc, ISurfaceAllocator* aDeallocator,
+ LayersBackend aBackend, TextureFlags aFlags) {
+#ifdef XP_MACOSX
+ if (aDesc.type() == SurfaceDescriptor::TSurfaceDescriptorMacIOSurface) {
+ const SurfaceDescriptorMacIOSurface& desc =
+ aDesc.get_SurfaceDescriptorMacIOSurface();
+ return MakeAndAddRef<MacIOSurfaceTextureHostBasic>(aFlags, desc);
+ }
+#endif
+ return CreateBackendIndependentTextureHost(aDesc, aDeallocator, aBackend,
+ aFlags);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/basic/TextureHostBasic.h b/gfx/layers/basic/TextureHostBasic.h
new file mode 100644
index 0000000000..f48bc89c97
--- /dev/null
+++ b/gfx/layers/basic/TextureHostBasic.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef MOZILLA_GFX_TEXTUREHOSTBASIC_H_
+#define MOZILLA_GFX_TEXTUREHOSTBASIC_H_
+
+#include "CompositableHost.h"
+#include "mozilla/layers/LayersSurfaces.h"
+#include "mozilla/layers/TextureHost.h"
+#include "mozilla/gfx/2D.h"
+
+namespace mozilla {
+namespace layers {
+
+already_AddRefed<TextureHost> CreateTextureHostBasic(
+ const SurfaceDescriptor& aDesc, ISurfaceAllocator* aDeallocator,
+ LayersBackend aBackend, TextureFlags aFlags);
+
+/**
+ * A texture source interface that can be used by the software Compositor.
+ */
+class TextureSourceBasic {
+ public:
+ TextureSourceBasic() : mFromYCBCR(false) {}
+ virtual ~TextureSourceBasic() = default;
+ virtual gfx::SourceSurface* GetSurface(gfx::DrawTarget* aTarget) = 0;
+ virtual void SetBufferTextureHost(BufferTextureHost* aTexture) {}
+ bool mFromYCBCR; // we to track sources from YCBCR so we can use a less
+ // accurate fast path for video
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_TEXTUREHOSTBASIC_H_
diff --git a/gfx/layers/basic/X11BasicCompositor.cpp b/gfx/layers/basic/X11BasicCompositor.cpp
new file mode 100644
index 0000000000..d9cf164571
--- /dev/null
+++ b/gfx/layers/basic/X11BasicCompositor.cpp
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "X11BasicCompositor.h"
+#include "gfxPlatform.h"
+#include "gfx2DGlue.h"
+#include "gfxXlibSurface.h"
+#include "gfxImageSurface.h"
+#include "mozilla/X11Util.h"
+
+namespace mozilla {
+using namespace mozilla::gfx;
+
+namespace layers {
+
+bool X11DataTextureSourceBasic::Update(gfx::DataSourceSurface* aSurface,
+ nsIntRegion* aDestRegion,
+ gfx::IntPoint* aSrcOffset) {
+ // Reallocate our internal X11 surface if we don't have a DrawTarget yet,
+ // or if we changed surface size or format since last update.
+ if (!mBufferDrawTarget ||
+ (aSurface->GetSize() != mBufferDrawTarget->GetSize()) ||
+ (aSurface->GetFormat() != mBufferDrawTarget->GetFormat())) {
+ RefPtr<gfxASurface> surf;
+ gfxImageFormat imageFormat =
+ SurfaceFormatToImageFormat(aSurface->GetFormat());
+ Display* display = DefaultXDisplay();
+ Screen* screen = DefaultScreenOfDisplay(display);
+ XRenderPictFormat* xrenderFormat =
+ gfxXlibSurface::FindRenderFormat(display, imageFormat);
+
+ if (xrenderFormat) {
+ surf = gfxXlibSurface::Create(screen, xrenderFormat, aSurface->GetSize());
+ }
+
+ if (!surf) {
+ NS_WARNING("Couldn't create native surface, fallback to image surface");
+ surf = new gfxImageSurface(aSurface->GetSize(), imageFormat);
+ }
+
+ mBufferDrawTarget =
+ gfxPlatform::CreateDrawTargetForSurface(surf, aSurface->GetSize());
+ }
+
+ // Image contents have changed, upload to our DrawTarget
+ // If aDestRegion is null, means we're updating the whole surface
+ // Note : Incremental update with a source offset is only used on Mac.
+ NS_ASSERTION(!aSrcOffset,
+ "SrcOffset should not be used with linux OMTC basic");
+
+ if (aDestRegion) {
+ for (auto iter = aDestRegion->RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& rect = iter.Get();
+ IntRect srcRect(rect.X(), rect.Y(), rect.Width(), rect.Height());
+ IntPoint dstPoint(rect.X(), rect.Y());
+
+ // We're uploading regions to our buffer, so let's just copy contents over
+ mBufferDrawTarget->CopySurface(aSurface, srcRect, dstPoint);
+ }
+ } else {
+ // We're uploading the whole buffer, so let's just copy the full surface
+ IntSize size = aSurface->GetSize();
+ mBufferDrawTarget->CopySurface(
+ aSurface, IntRect(0, 0, size.width, size.height), IntPoint(0, 0));
+ }
+
+ return true;
+}
+
+TextureSourceBasic* X11DataTextureSourceBasic::AsSourceBasic() { return this; }
+
+IntSize X11DataTextureSourceBasic::GetSize() const {
+ if (!mBufferDrawTarget) {
+ NS_WARNING("Trying to query the size of an uninitialized TextureSource");
+ return IntSize(0, 0);
+ }
+ return mBufferDrawTarget->GetSize();
+}
+
+gfx::SurfaceFormat X11DataTextureSourceBasic::GetFormat() const {
+ if (!mBufferDrawTarget) {
+ NS_WARNING("Trying to query the format of an uninitialized TextureSource");
+ return gfx::SurfaceFormat::UNKNOWN;
+ }
+ return mBufferDrawTarget->GetFormat();
+}
+
+SourceSurface* X11DataTextureSourceBasic::GetSurface(DrawTarget* aTarget) {
+ RefPtr<gfx::SourceSurface> surface;
+ if (mBufferDrawTarget) {
+ surface = mBufferDrawTarget->Snapshot();
+ return surface.get();
+ }
+ return nullptr;
+}
+
+void X11DataTextureSourceBasic::DeallocateDeviceData() {
+ mBufferDrawTarget = nullptr;
+}
+
+already_AddRefed<DataTextureSource> X11BasicCompositor::CreateDataTextureSource(
+ TextureFlags aFlags) {
+ RefPtr<DataTextureSource> result = new X11DataTextureSourceBasic();
+ return result.forget();
+}
+
+void X11BasicCompositor::EndFrame() {
+ BasicCompositor::EndFrame();
+ XFlush(DefaultXDisplay());
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/basic/X11BasicCompositor.h b/gfx/layers/basic/X11BasicCompositor.h
new file mode 100644
index 0000000000..44b6acbd93
--- /dev/null
+++ b/gfx/layers/basic/X11BasicCompositor.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef MOZILLA_GFX_X11BASICCOMPOSITOR_H
+#define MOZILLA_GFX_X11BASICCOMPOSITOR_H
+
+#include "mozilla/layers/BasicCompositor.h"
+#include "mozilla/layers/X11TextureSourceBasic.h"
+#include "mozilla/layers/TextureHostBasic.h"
+#include "gfxXlibSurface.h"
+#include "mozilla/gfx/2D.h"
+
+namespace mozilla {
+namespace layers {
+
+// TextureSource for Image-backed surfaces.
+class X11DataTextureSourceBasic : public DataTextureSource,
+ public TextureSourceBasic {
+ public:
+ X11DataTextureSourceBasic() = default;
+
+ const char* Name() const override { return "X11DataTextureSourceBasic"; }
+
+ bool Update(gfx::DataSourceSurface* aSurface,
+ nsIntRegion* aDestRegion = nullptr,
+ gfx::IntPoint* aSrcOffset = nullptr) override;
+
+ TextureSourceBasic* AsSourceBasic() override;
+
+ gfx::SourceSurface* GetSurface(gfx::DrawTarget* aTarget) override;
+
+ void DeallocateDeviceData() override;
+
+ gfx::IntSize GetSize() const override;
+
+ gfx::SurfaceFormat GetFormat() const override;
+
+ private:
+ // We are going to buffer layer content on this xlib draw target
+ RefPtr<mozilla::gfx::DrawTarget> mBufferDrawTarget;
+};
+
+class X11BasicCompositor : public BasicCompositor {
+ public:
+ explicit X11BasicCompositor(CompositorBridgeParent* aParent,
+ widget::CompositorWidget* aWidget)
+ : BasicCompositor(aParent, aWidget) {}
+
+ already_AddRefed<DataTextureSource> CreateDataTextureSource(
+ TextureFlags aFlags = TextureFlags::NO_FLAGS) override;
+
+ already_AddRefed<DataTextureSource> CreateDataTextureSourceAround(
+ gfx::DataSourceSurface* aSurface) override {
+ return nullptr;
+ }
+
+ void EndFrame() override;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_X11BASICCOMPOSITOR_H */
diff --git a/gfx/layers/basic/X11TextureSourceBasic.cpp b/gfx/layers/basic/X11TextureSourceBasic.cpp
new file mode 100644
index 0000000000..b987f1ba1b
--- /dev/null
+++ b/gfx/layers/basic/X11TextureSourceBasic.cpp
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "X11TextureSourceBasic.h"
+#include "gfxXlibSurface.h"
+#include "gfx2DGlue.h"
+
+namespace mozilla::layers {
+
+using namespace mozilla::gfx;
+
+X11TextureSourceBasic::X11TextureSourceBasic(BasicCompositor* aCompositor,
+ gfxXlibSurface* aSurface)
+ : mSurface(aSurface) {}
+
+IntSize X11TextureSourceBasic::GetSize() const { return mSurface->GetSize(); }
+
+SurfaceFormat X11TextureSourceBasic::GetFormat() const {
+ gfxContentType type = mSurface->GetContentType();
+ return X11TextureSourceBasic::ContentTypeToSurfaceFormat(type);
+}
+
+SourceSurface* X11TextureSourceBasic::GetSurface(DrawTarget* aTarget) {
+ if (!mSourceSurface) {
+ mSourceSurface = Factory::CreateSourceSurfaceForCairoSurface(
+ mSurface->CairoSurface(), GetSize(), GetFormat());
+ }
+ return mSourceSurface;
+}
+
+SurfaceFormat X11TextureSourceBasic::ContentTypeToSurfaceFormat(
+ gfxContentType aType) {
+ switch (aType) {
+ case gfxContentType::COLOR:
+ return SurfaceFormat::B8G8R8X8;
+ case gfxContentType::ALPHA:
+ return SurfaceFormat::A8;
+ case gfxContentType::COLOR_ALPHA:
+ return SurfaceFormat::B8G8R8A8;
+ default:
+ return SurfaceFormat::UNKNOWN;
+ }
+}
+
+} // namespace mozilla::layers
diff --git a/gfx/layers/basic/X11TextureSourceBasic.h b/gfx/layers/basic/X11TextureSourceBasic.h
new file mode 100644
index 0000000000..7d89c935eb
--- /dev/null
+++ b/gfx/layers/basic/X11TextureSourceBasic.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef MOZILLA_GFX_X11TEXTURESOURCEBASIC__H
+#define MOZILLA_GFX_X11TEXTURESOURCEBASIC__H
+
+#include "mozilla/layers/BasicCompositor.h"
+#include "mozilla/layers/TextureHostBasic.h"
+#include "mozilla/layers/X11TextureHost.h"
+#include "mozilla/gfx/2D.h"
+
+namespace mozilla {
+namespace layers {
+
+class BasicCompositor;
+
+// TextureSource for Xlib-backed surfaces.
+class X11TextureSourceBasic : public TextureSourceBasic,
+ public X11TextureSource {
+ public:
+ X11TextureSourceBasic(BasicCompositor* aCompositor, gfxXlibSurface* aSurface);
+
+ virtual const char* Name() const override { return "X11TextureSourceBasic"; }
+
+ virtual X11TextureSourceBasic* AsSourceBasic() override { return this; }
+
+ virtual gfx::IntSize GetSize() const override;
+
+ virtual gfx::SurfaceFormat GetFormat() const override;
+
+ virtual gfx::SourceSurface* GetSurface(gfx::DrawTarget* aTarget) override;
+
+ virtual void DeallocateDeviceData() override {}
+
+ virtual void Updated() override {}
+
+ static gfx::SurfaceFormat ContentTypeToSurfaceFormat(gfxContentType aType);
+
+ protected:
+ RefPtr<gfxXlibSurface> mSurface;
+ RefPtr<gfx::SourceSurface> mSourceSurface;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_X11TEXTURESOURCEBASIC__H