diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /gfx/layers/basic | |
parent | Initial commit. (diff) | |
download | firefox-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 '')
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 |