diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /gfx/layers/basic | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'gfx/layers/basic')
27 files changed, 4954 insertions, 0 deletions
diff --git a/gfx/layers/basic/AutoMaskData.h b/gfx/layers/basic/AutoMaskData.h new file mode 100644 index 000000000..7bf7f9b3c --- /dev/null +++ b/gfx/layers/basic/AutoMaskData.h @@ -0,0 +1,60 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#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() { } + ~AutoMoz2DMaskData() { } + + 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 000000000..83c5c272e --- /dev/null +++ b/gfx/layers/basic/BasicCanvasLayer.cpp @@ -0,0 +1,141 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "BasicCanvasLayer.h" +#include "AsyncCanvasRenderer.h" +#include "basic/BasicLayers.h" // for BasicLayerManager +#include "basic/BasicLayersImpl.h" // for GetEffectiveOperator +#include "mozilla/mozalloc.h" // for operator new +#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 { + +already_AddRefed<SourceSurface> +BasicCanvasLayer::UpdateSurface() +{ + if (mAsyncRenderer) { + MOZ_ASSERT(!mBufferProvider); + MOZ_ASSERT(!mGLContext); + return mAsyncRenderer->GetSurface(); + } + + if (!mGLContext) { + return nullptr; + } + + SharedSurface* frontbuffer = nullptr; + if (mGLFrontbuffer) { + frontbuffer = mGLFrontbuffer.get(); + } else { + GLScreenBuffer* screen = mGLContext->Screen(); + const auto& front = screen->Front(); + if (front) { + frontbuffer = front->Surf(); + } + } + + if (!frontbuffer) { + NS_WARNING("Null frame received."); + return nullptr; + } + + IntSize readSize(frontbuffer->mSize); + SurfaceFormat format = (GetContentFlags() & CONTENT_OPAQUE) + ? SurfaceFormat::B8G8R8X8 + : SurfaceFormat::B8G8R8A8; + bool needsPremult = frontbuffer->mHasAlpha && !mIsAlphaPremultiplied; + + RefPtr<DataSourceSurface> resultSurf = GetTempSurface(readSize, format); + // There will already be a warning from inside of GetTempSurface, but + // it doesn't hurt to complain: + if (NS_WARN_IF(!resultSurf)) { + return nullptr; + } + + // Readback handles Flush/MarkDirty. + mGLContext->Readback(frontbuffer, resultSurf); + if (needsPremult) { + gfxUtils::PremultiplyDataSurface(resultSurf, resultSurf); + } + MOZ_ASSERT(resultSurf); + + return resultSurf.forget(); +} + +void +BasicCanvasLayer::Paint(DrawTarget* aDT, + const Point& aDeviceOffset, + Layer* aMaskLayer) +{ + if (IsHidden()) + return; + + RefPtr<SourceSurface> surface; + if (IsDirty()) { + Painted(); + + FirePreTransactionCallback(); + surface = UpdateSurface(); + FireDidTransactionCallback(); + } + + bool bufferPoviderSnapshot = false; + if (!surface && mBufferProvider) { + surface = mBufferProvider->BorrowSnapshot(); + bufferPoviderSnapshot = !!surface; + } + + if (!surface) { + return; + } + + const bool needsYFlip = (mOriginPos == gl::OriginPos::BottomLeft); + + Matrix oldTM; + if (needsYFlip) { + oldTM = 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 (needsYFlip) { + aDT->SetTransform(oldTM); + } + + if (bufferPoviderSnapshot) { + mBufferProvider->ReturnSnapshot(surface.forget()); + } +} + +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 000000000..a63d2b8c0 --- /dev/null +++ b/gfx/layers/basic/BasicCanvasLayer.h @@ -0,0 +1,51 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef GFX_BASICCANVASLAYER_H +#define GFX_BASICCANVASLAYER_H + +#include "BasicImplData.h" // for BasicImplData +#include "BasicLayers.h" // for BasicLayerManager +#include "CopyableCanvasLayer.h" // for CopyableCanvasLayer +#include "Layers.h" // for CanvasLayer, etc +#include "nsDebug.h" // for NS_ASSERTION +#include "nsRegion.h" // for nsIntRegion + +namespace mozilla { +namespace layers { + +class BasicCanvasLayer : public CopyableCanvasLayer, + public BasicImplData +{ +public: + explicit BasicCanvasLayer(BasicLayerManager* aLayerManager) : + CopyableCanvasLayer(aLayerManager, static_cast<BasicImplData*>(this)) + { } + + virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override + { + NS_ASSERTION(BasicManager()->InConstruction(), + "Can only set properties in construction phase"); + CanvasLayer::SetVisibleRegion(aRegion); + } + + virtual void Paint(gfx::DrawTarget* aDT, + const gfx::Point& aDeviceOffset, + Layer* aMaskLayer) override; + +protected: + + already_AddRefed<gfx::SourceSurface> UpdateSurface(); + + BasicLayerManager* BasicManager() + { + return static_cast<BasicLayerManager*>(mManager); + } +}; + +} // namespace layers +} // namespace mozilla + +#endif diff --git a/gfx/layers/basic/BasicColorLayer.cpp b/gfx/layers/basic/BasicColorLayer.cpp new file mode 100644 index 000000000..182bc785b --- /dev/null +++ b/gfx/layers/basic/BasicColorLayer.cpp @@ -0,0 +1,83 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "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: + virtual ~BasicColorLayer() + { + MOZ_COUNT_DTOR(BasicColorLayer); + } + +public: + virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override + { + NS_ASSERTION(BasicManager()->InConstruction(), + "Can only set properties in construction phase"); + ColorLayer::SetVisibleRegion(aRegion); + } + + virtual 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 000000000..1ff27f795 --- /dev/null +++ b/gfx/layers/basic/BasicCompositor.cpp @@ -0,0 +1,996 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- +* This Source Code Form is subject to the terms of the Mozilla Public +* License, v. 2.0. If a copy of the MPL was not distributed with this +* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "BasicCompositor.h" +#include "BasicLayersImpl.h" // for FillRectWithMask +#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/Tools.h" +#include "mozilla/gfx/ssse3-scaler.h" +#include "mozilla/layers/ImageDataSerializer.h" +#include "mozilla/SSE.h" +#include "gfxUtils.h" +#include "YCbCrUtils.h" +#include <algorithm> +#include "ImageContainer.h" +#include "gfxPrefs.h" + +namespace mozilla { +using namespace mozilla::gfx; + +namespace layers { + +class DataTextureSourceBasic : public DataTextureSource + , public TextureSourceBasic +{ +public: + virtual const char* Name() const override { return "DataTextureSourceBasic"; } + + explicit DataTextureSourceBasic(DataSourceSurface* aSurface) + : mSurface(aSurface) + , mWrappingExistingData(!!aSurface) + {} + + virtual 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; + } + + virtual TextureSourceBasic* AsSourceBasic() override { return this; } + + virtual gfx::SourceSurface* GetSurface(DrawTarget* aTarget) override { return mSurface; } + + SurfaceFormat GetFormat() const override + { + return mSurface ? mSurface->GetFormat() : gfx::SurfaceFormat::UNKNOWN; + } + + virtual IntSize GetSize() const override + { + return mSurface ? mSurface->GetSize() : gfx::IntSize(0, 0); + } + + virtual bool Update(gfx::DataSourceSurface* aSurface, + nsIntRegion* aDestRegion = nullptr, + gfx::IntPoint* aSrcOffset = nullptr) override + { + MOZ_ASSERT(!mWrappingExistingData); + if (mWrappingExistingData) { + return false; + } + mSurface = aSurface; + return true; + } + + virtual 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: + virtual const char* Name() const override { return "WrappingTextureSourceYCbCrBasic"; } + + explicit WrappingTextureSourceYCbCrBasic(BufferTextureHost* aTexture) + : mTexture(aTexture) + , mSize(aTexture->GetSize()) + , mNeedsUpdate(true) + { + mFromYCBCR = true; + } + + virtual DataTextureSource* AsDataTextureSource() override + { + return this; + } + + virtual TextureSourceBasic* AsSourceBasic() override { return this; } + + virtual WrappingTextureSourceYCbCrBasic* AsWrappingTextureSourceYCbCrBasic() override { return this; } + + virtual 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; + } + + virtual IntSize GetSize() const override + { + return mSize; + } + + virtual bool Update(gfx::DataSourceSurface* aSurface, + nsIntRegion* aDestRegion = nullptr, + gfx::IntPoint* aSrcOffset = nullptr) override + { + return false; + } + + virtual void DeallocateDeviceData() override + { + mTexture = nullptr; + mSurface = nullptr; + SetUpdateSerial(0); + } + + virtual 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; +}; + +BasicCompositor::BasicCompositor(CompositorBridgeParent* aParent, widget::CompositorWidget* aWidget) + : Compositor(aWidget, aParent) + , mDidExternalComposition(false) + , mIsPendingEndRemoteDrawing(false) +{ + MOZ_COUNT_CTOR(BasicCompositor); + + mMaxTextureSize = Factory::GetMaxSurfaceSize(gfxVars::ContentBackend()); +} + +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(0, 0, mSize.width, mSize.height)); + mClearOnBind = false; + } +} + +void BasicCompositor::DetachWidget() +{ + if (mWidget) { + if (mIsPendingEndRemoteDrawing) { + // Force to end previous remote drawing. + TryToEndRemoteDrawing(/* aForceToEnd */ true); + MOZ_ASSERT(!mIsPendingEndRemoteDrawing); + } + mWidget->CleanupRemoteDrawing(); + } + Compositor::DetachWidget(); +} + +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.width != 0 && aRect.height != 0, "Trying to create a render target of invalid size"); + + if (aRect.width * aRect.height == 0) { + return nullptr; + } + + RefPtr<DrawTarget> target = mDrawTarget->CreateSimilarDrawTarget(aRect.Size(), SurfaceFormat::B8G8R8A8); + + if (!target) { + return nullptr; + } + + RefPtr<BasicCompositingRenderTarget> rt = new BasicCompositingRenderTarget(target, aRect); + + 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::CreateRenderTargetForWindow(const LayoutDeviceIntRect& aRect, const LayoutDeviceIntRect& aClearRect, BufferMode aBufferMode) +{ + MOZ_ASSERT(mDrawTarget); + MOZ_ASSERT(aRect.width != 0 && aRect.height != 0, "Trying to create a render target of invalid size"); + + if (aRect.width * aRect.height == 0) { + return nullptr; + } + + RefPtr<BasicCompositingRenderTarget> rt; + IntRect rect = aRect.ToUnknownRect(); + + if (aBufferMode != BufferMode::BUFFER_NONE) { + RefPtr<DrawTarget> target = mWidget->GetBackBufferDrawTarget(mDrawTarget, aRect, aClearRect); + if (!target) { + return nullptr; + } + MOZ_ASSERT(target != mDrawTarget); + rt = new BasicCompositingRenderTarget(target, rect); + } else { + IntRect windowRect = rect; + // Adjust bounds rect to account for new origin at (0, 0). + if (windowRect.Size() != mDrawTarget->GetSize()) { + windowRect.ExpandToEnclose(IntPoint(0, 0)); + } + rt = new BasicCompositingRenderTarget(mDrawTarget, windowRect); + if (!aClearRect.IsEmpty()) { + IntRect clearRect = aRect.ToUnknownRect(); + mDrawTarget->ClearRect(Rect(clearRect - rt->GetOrigin())); + } + } + + 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; +} + +static void +DrawSurfaceWithTextureCoords(DrawTarget *aDest, + const gfx::Rect& aDestRect, + SourceSurface *aSource, + const gfx::Rect& aTextureCoords, + gfx::SamplingFilter aSamplingFilter, + const DrawOptions& aOptions, + SourceSurface *aMask, + const 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; + + FillRectWithMask(aDest, 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); + + 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 true; + } 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); + 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) +{ + RefPtr<DrawTarget> buffer = mRenderTarget->mDrawTarget; + + // For 2D drawing, |dest| and |buffer| are the same surface. For 3D drawing, + // |dest| is a temporary surface. + RefPtr<DrawTarget> dest = buffer; + + AutoRestoreTransform autoRestoreTransform(dest); + + Matrix newTransform; + Rect transformBounds; + Matrix4x4 new3DTransform; + IntPoint offset = mRenderTarget->GetOrigin(); + + if (aTransform.Is2D()) { + 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(offset.x, offset.y, buffer->GetSize().width, buffer->GetSize().height)); + 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); + } + + // XXX the transform is probably just an integer offset so this whole + // business here is a bit silly. + Rect transformedClipRect = buffer->GetTransform().TransformBounds(Rect(aClipRect)); + + buffer->PushClipRect(Rect(aClipRect)); + + 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; + } + + 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); + } + + FillRectWithMask(dest, aRect, effectSolidColor->mColor, + DrawOptions(aOpacity, blendMode), 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, transformedClipRect, + 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, transformedClipRect, + dest, buffer)) { + // we succeeded in scaling + } else { + DrawSurfaceWithTextureCoords(dest, aRect, + source->GetSurface(dest), + texturedEffect->mTextureCoords, + texturedEffect->mSamplingFilter, + DrawOptions(aOpacity, blendMode), + 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, aRect, + premultData, + texturedEffect->mTextureCoords, + texturedEffect->mSamplingFilter, + DrawOptions(aOpacity, blendMode), + sourceMask, &maskTransform); + } + } else { + gfxDevCrash(LogReason::IncompatibleBasicTexturedEffect) << "Bad for basic with " << texturedEffect->mTexture->Name() << " and " << gfx::hexa(sourceMask); + } + + break; + } + case EffectTypes::YCBCR: { + NS_RUNTIMEABORT("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, aRect, + sourceSurf, + effectRenderTarget->mTextureCoords, + effectRenderTarget->mSamplingFilter, + DrawOptions(aOpacity, blendMode), + sourceMask, &maskTransform); + break; + } + case EffectTypes::COMPONENT_ALPHA: { + NS_RUNTIMEABORT("Can't (easily) support component alpha with BasicCompositor!"); + break; + } + default: { + NS_RUNTIMEABORT("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); +} + +void +BasicCompositor::BeginFrame(const nsIntRegion& aInvalidRegion, + const gfx::IntRect *aClipRectIn, + const gfx::IntRect& aRenderBounds, + const nsIntRegion& aOpaqueRegion, + gfx::IntRect *aClipRectOut /* = nullptr */, + gfx::IntRect *aRenderBoundsOut /* = nullptr */) +{ + if (mIsPendingEndRemoteDrawing) { + // Force to end previous remote drawing. + TryToEndRemoteDrawing(/* aForceToEnd */ true); + MOZ_ASSERT(!mIsPendingEndRemoteDrawing); + } + + LayoutDeviceIntRect intRect(LayoutDeviceIntPoint(), mWidget->GetClientSize()); + IntRect rect = IntRect(0, 0, intRect.width, intRect.height); + + LayoutDeviceIntRegion invalidRegionSafe; + if (mDidExternalComposition) { + // We do not know rendered region during external composition, just redraw + // whole widget. + invalidRegionSafe = intRect; + mDidExternalComposition = false; + } else { + // Sometimes the invalid region is larger than we want to draw. + invalidRegionSafe.And( + LayoutDeviceIntRegion::FromUnknownRegion(aInvalidRegion), intRect); + } + + mInvalidRegion = invalidRegionSafe; + mInvalidRect = mInvalidRegion.GetBounds(); + + if (aRenderBoundsOut) { + *aRenderBoundsOut = IntRect(); + } + + BufferMode bufferMode = BufferMode::BUFFERED; + if (mTarget) { + // If we have a copy target, then we don't have a widget-provided mDrawTarget (currently). Use a dummy + // placeholder so that CreateRenderTarget() works. This is only used to create a new buffered + // draw target that we composite into, then copy the results the destination. + mDrawTarget = mTarget; + bufferMode = BufferMode::BUFFER_NONE; + } else { + // StartRemoteDrawingInRegion can mutate mInvalidRegion. + mDrawTarget = mWidget->StartRemoteDrawingInRegion(mInvalidRegion, &bufferMode); + if (!mDrawTarget) { + return; + } + mInvalidRect = mInvalidRegion.GetBounds(); + if (mInvalidRect.IsEmpty()) { + mWidget->EndRemoteDrawingInRegion(mDrawTarget, mInvalidRegion); + return; + } + } + + if (!mDrawTarget || mInvalidRect.IsEmpty()) { + return; + } + + LayoutDeviceIntRect clearRect; + if (!aOpaqueRegion.IsEmpty()) { + LayoutDeviceIntRegion clearRegion = mInvalidRegion; + clearRegion.SubOut(LayoutDeviceIntRegion::FromUnknownRegion(aOpaqueRegion)); + clearRect = clearRegion.GetBounds(); + } else { + clearRect = mInvalidRect; + } + + // Prevent CreateRenderTargetForWindow from clearing unwanted area. + gfxUtils::ClipToRegion(mDrawTarget, + mInvalidRegion.ToUnknownRegion()); + + // Setup an intermediate render target to buffer all compositing. We will + // copy this into mDrawTarget (the widget), and/or mTarget in EndFrame() + RefPtr<CompositingRenderTarget> target = + CreateRenderTargetForWindow(mInvalidRect, + clearRect, + bufferMode); + + mDrawTarget->PopClip(); + + if (!target) { + if (!mTarget) { + mWidget->EndRemoteDrawingInRegion(mDrawTarget, mInvalidRegion); + } + return; + } + SetRenderTarget(target); + + // We only allocate a surface sized to the invalidated region, so we need to + // translate future coordinates. + mRenderTarget->mDrawTarget->SetTransform(Matrix::Translation(-mRenderTarget->GetOrigin())); + + gfxUtils::ClipToRegion(mRenderTarget->mDrawTarget, + mInvalidRegion.ToUnknownRegion()); + + if (aRenderBoundsOut) { + *aRenderBoundsOut = rect; + } + + if (aClipRectIn) { + mRenderTarget->mDrawTarget->PushClipRect(Rect(*aClipRectIn)); + } else { + mRenderTarget->mDrawTarget->PushClipRect(Rect(rect)); + if (aClipRectOut) { + *aClipRectOut = rect; + } + } +} + +void +BasicCompositor::EndFrame() +{ + Compositor::EndFrame(); + + // Pop aClipRectIn/bounds rect + mRenderTarget->mDrawTarget->PopClip(); + + if (gfxPrefs::WidgetUpdateFlashing()) { + float r = float(rand()) / RAND_MAX; + float g = float(rand()) / RAND_MAX; + float b = float(rand()) / RAND_MAX; + // We're still clipped to mInvalidRegion, so just fill the bounds. + mRenderTarget->mDrawTarget->FillRect( + IntRectToRect(mInvalidRegion.GetBounds()).ToUnknownRect(), + ColorPattern(Color(r, g, b, 0.2f))); + } + + // Pop aInvalidregion + mRenderTarget->mDrawTarget->PopClip(); + + TryToEndRemoteDrawing(); +} + +void +BasicCompositor::TryToEndRemoteDrawing(bool aForceToEnd) +{ + if (mIsDestroyed || !mRenderTarget) { + return; + } + + // It it is not a good timing for EndRemoteDrawing, defter to call it. + if (!aForceToEnd && !mTarget && NeedsToDeferEndRemoteDrawing()) { + mIsPendingEndRemoteDrawing = true; + + const uint32_t retryMs = 2; + RefPtr<BasicCompositor> self = this; + RefPtr<Runnable> runnable = NS_NewRunnableFunction([self]() { + self->TryToEndRemoteDrawing(); + }); + MessageLoop::current()->PostDelayedTask(runnable.forget(), retryMs); + return; + } + + if (mRenderTarget->mDrawTarget != mDrawTarget) { + // Note: Most platforms require us to buffer drawing to the widget surface. + // That's why we don't draw to mDrawTarget directly. + RefPtr<SourceSurface> source; + if (mRenderTarget->mDrawTarget != mDrawTarget) { + source = mWidget->EndBackBufferDrawing(); + } else { + source = mRenderTarget->mDrawTarget->Snapshot(); + } + RefPtr<DrawTarget> dest(mTarget ? mTarget : mDrawTarget); + + nsIntPoint offset = mTarget ? mTargetBounds.TopLeft() : nsIntPoint(); + + // 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 blank + // pixels. + for (auto iter = mInvalidRegion.RectIter(); !iter.Done(); iter.Next()) { + const LayoutDeviceIntRect& r = iter.Get(); + dest->CopySurface(source, + IntRect(r.x, r.y, r.width, r.height) - mRenderTarget->GetOrigin(), + IntPoint(r.x, r.y) - offset); + } + } + + if (aForceToEnd || !mTarget) { + mWidget->EndRemoteDrawingInRegion(mDrawTarget, mInvalidRegion); + } + + mDrawTarget = nullptr; + mRenderTarget = nullptr; + mIsPendingEndRemoteDrawing = false; +} + +bool +BasicCompositor::NeedsToDeferEndRemoteDrawing() +{ + MOZ_ASSERT(mDrawTarget); + MOZ_ASSERT(mRenderTarget); + + if (mTarget || mRenderTarget->mDrawTarget == mDrawTarget) { + return false; + } + + return mWidget->NeedsToDeferEndRemoteDrawing(); +} + +void +BasicCompositor::FinishPendingComposite() +{ + TryToEndRemoteDrawing(/* aForceToEnd */ true); +} + +void +BasicCompositor::EndFrameForExternalComposition(const gfx::Matrix& aTransform) +{ + MOZ_ASSERT(!mTarget); + MOZ_ASSERT(!mDrawTarget); + MOZ_ASSERT(!mRenderTarget); + + mDidExternalComposition = true; +} + +BasicCompositor* +AssertBasicCompositor(Compositor* aCompositor) +{ + BasicCompositor* compositor = aCompositor ? aCompositor->AsBasicCompositor() + : nullptr; + MOZ_DIAGNOSTIC_ASSERT(!!compositor); + return compositor; +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/basic/BasicCompositor.h b/gfx/layers/basic/BasicCompositor.h new file mode 100644 index 000000000..73f3e82c3 --- /dev/null +++ b/gfx/layers/basic/BasicCompositor.h @@ -0,0 +1,162 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- +* This Source Code Form is subject to the terms of the Mozilla Public +* License, v. 2.0. If a copy of the MPL was not distributed with this +* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#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" + +namespace mozilla { +namespace layers { + +class BasicCompositingRenderTarget : public CompositingRenderTarget +{ +public: + BasicCompositingRenderTarget(gfx::DrawTarget* aDrawTarget, const gfx::IntRect& aRect) + : CompositingRenderTarget(aRect.TopLeft()) + , mDrawTarget(aDrawTarget) + , mSize(aRect.Size()) + { } + + virtual const char* Name() const override { return "BasicCompositingRenderTarget"; } + + virtual gfx::IntSize GetSize() const override { return mSize; } + + void BindRenderTarget(); + + virtual gfx::SurfaceFormat GetFormat() const override + { + return mDrawTarget ? mDrawTarget->GetFormat() + : gfx::SurfaceFormat(gfx::SurfaceFormat::UNKNOWN); + } + + RefPtr<gfx::DrawTarget> mDrawTarget; + gfx::IntSize mSize; +}; + +class BasicCompositor : public Compositor +{ +public: + explicit BasicCompositor(CompositorBridgeParent* aParent, widget::CompositorWidget* aWidget); + +protected: + virtual ~BasicCompositor(); + +public: + + virtual BasicCompositor* AsBasicCompositor() override { return this; } + + virtual bool Initialize(nsCString* const out_failureReason) override; + + virtual void DetachWidget() override; + + virtual TextureFactoryIdentifier GetTextureFactoryIdentifier() override; + + virtual already_AddRefed<CompositingRenderTarget> + CreateRenderTarget(const gfx::IntRect &aRect, SurfaceInitMode aInit) override; + + virtual already_AddRefed<CompositingRenderTarget> + CreateRenderTargetFromSource(const gfx::IntRect &aRect, + const CompositingRenderTarget *aSource, + const gfx::IntPoint &aSourcePoint) override; + + virtual already_AddRefed<CompositingRenderTarget> + CreateRenderTargetForWindow(const LayoutDeviceIntRect& aRect, + const LayoutDeviceIntRect& aClearRect, + BufferMode aBufferMode); + + virtual already_AddRefed<DataTextureSource> + CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) override; + + virtual already_AddRefed<DataTextureSource> + CreateDataTextureSourceAround(gfx::DataSourceSurface* aSurface) override; + + virtual already_AddRefed<DataTextureSource> + CreateDataTextureSourceAroundYCbCr(TextureHost* aTexture) override; + + virtual bool SupportsEffect(EffectTypes aEffect) override; + + virtual void SetRenderTarget(CompositingRenderTarget *aSource) override + { + mRenderTarget = static_cast<BasicCompositingRenderTarget*>(aSource); + mRenderTarget->BindRenderTarget(); + } + virtual CompositingRenderTarget* GetCurrentRenderTarget() const override + { + return mRenderTarget; + } + + virtual 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; + + virtual void ClearRect(const gfx::Rect& aRect) override; + + virtual void BeginFrame(const nsIntRegion& aInvalidRegion, + const gfx::IntRect *aClipRectIn, + const gfx::IntRect& aRenderBounds, + const nsIntRegion& aOpaqueRegion, + gfx::IntRect *aClipRectOut = nullptr, + gfx::IntRect *aRenderBoundsOut = nullptr) override; + virtual void EndFrame() override; + virtual void EndFrameForExternalComposition(const gfx::Matrix& aTransform) override; + + virtual bool SupportsPartialTextureUpdate() override { return true; } + virtual bool CanUseCanvasLayerForSize(const gfx::IntSize &aSize) override { return true; } + virtual int32_t GetMaxTextureSize() const override; + virtual void SetDestinationSurfaceSize(const gfx::IntSize& aSize) override { } + + virtual void SetScreenRenderOffset(const ScreenPoint& aOffset) override { + } + + virtual void MakeCurrent(MakeCurrentFlags aFlags = 0) override { } + +#ifdef MOZ_DUMP_PAINTING + virtual const char* Name() const override { return "Basic"; } +#endif // MOZ_DUMP_PAINTING + + virtual LayersBackend GetBackendType() const override { + return LayersBackend::LAYERS_BASIC; + } + + gfx::DrawTarget *GetDrawTarget() { return mDrawTarget; } + + virtual bool IsPendingComposite() override + { + return mIsPendingEndRemoteDrawing; + } + + virtual void FinishPendingComposite() override; + +private: + + void TryToEndRemoteDrawing(bool aForceToEnd = false); + + bool NeedsToDeferEndRemoteDrawing(); + + // The final destination surface + RefPtr<gfx::DrawTarget> mDrawTarget; + // The current render target for drawing + RefPtr<BasicCompositingRenderTarget> mRenderTarget; + + LayoutDeviceIntRect mInvalidRect; + LayoutDeviceIntRegion mInvalidRegion; + bool mDidExternalComposition; + + uint32_t mMaxTextureSize; + bool mIsPendingEndRemoteDrawing; +}; + +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 000000000..499e202c4 --- /dev/null +++ b/gfx/layers/basic/BasicContainerLayer.cpp @@ -0,0 +1,172 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "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() +{ + while (mFirstChild) { + ContainerLayer::RemoveChild(mFirstChild); + } + + 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().ToUnknownRegion().GetBounds() + 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 000000000..c8227f6e9 --- /dev/null +++ b/gfx/layers/basic/BasicContainerLayer.h @@ -0,0 +1,106 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#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: + virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override + { + NS_ASSERTION(BasicManager()->InConstruction(), + "Can only set properties in construction phase"); + ContainerLayer::SetVisibleRegion(aRegion); + } + virtual 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); + } + + virtual bool RemoveChild(Layer* aChild) override + { + if (!BasicManager()->InConstruction()) { + NS_ERROR("Can only set properties in construction phase"); + return false; + } + return ContainerLayer::RemoveChild(aChild); + } + + virtual 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); + } + + virtual 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; } + + virtual 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. + */ + virtual 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 000000000..0bb6b132d --- /dev/null +++ b/gfx/layers/basic/BasicImageLayer.cpp @@ -0,0 +1,119 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "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 { +namespace 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: + virtual ~BasicImageLayer() + { + MOZ_COUNT_DTOR(BasicImageLayer); + } + +public: + virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override + { + NS_ASSERTION(BasicManager()->InConstruction(), + "Can only set properties in construction phase"); + ImageLayer::SetVisibleRegion(aRegion); + } + + virtual void Paint(DrawTarget* aDT, + const gfx::Point& aDeviceOffset, + Layer* aMaskLayer) override; + + virtual 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(); + 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 layers +} // namespace mozilla diff --git a/gfx/layers/basic/BasicImages.cpp b/gfx/layers/basic/BasicImages.cpp new file mode 100644 index 000000000..ed9447207 --- /dev/null +++ b/gfx/layers/basic/BasicImages.cpp @@ -0,0 +1,178 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <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/mozalloc.h" // for operator delete[], etc +#include "mozilla/RefPtr.h" +#include "mozilla/UniquePtr.h" +#include "nsAutoRef.h" // for nsCountedRef +#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 "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(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() {} + + virtual RefPtr<PlanarYCbCrImage> + CreatePlanarYCbCrImage(const gfx::IntSize& aScaleHint, BufferRecycleBin* aRecycleBin) + { + 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; + } + + gfxImageFormat iFormat = gfx::SurfaceFormatToImageFormat(format); + mStride = gfxASurface::FormatStrideForWidth(iFormat, size.width); + mDecodedBuffer = AllocateBuffer(size.height * mStride); + if (!mDecodedBuffer) { + // out of memory + return false; + } + + gfx::ConvertYCbCrToRGB(aData, format, size, mDecodedBuffer.get(), mStride); + SetOffscreenFormat(iFormat); + 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(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 000000000..2ffa310a9 --- /dev/null +++ b/gfx/layers/basic/BasicImplData.h @@ -0,0 +1,132 @@ +/* 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 "Layers.h" // for Layer (ptr only), etc +#include "gfxContext.h" // for gfxContext, etc +#include "nsDebug.h" // for NS_ASSERTION +#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc +#include "mozilla/gfx/Types.h" + +namespace mozilla { +namespace layers { + +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); + } + virtual ~BasicImplData() + { + MOZ_COUNT_DTOR(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 000000000..41c37dc8e --- /dev/null +++ b/gfx/layers/basic/BasicLayerManager.cpp @@ -0,0 +1,991 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <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 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 "gfxPrefs.h" // for gfxPrefs +#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/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/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 "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(); + + gfxMatrix currentMatrix = aContext->CurrentMatrix(); + aContext->SetMatrix(gfxMatrix()); + 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. + gfxMatrix oldMat = aContext->CurrentMatrix(); + aContext->SetMatrix(gfxMatrix()); + gfxRect rect = aContext->GetClipExtents(); + aContext->SetMatrix(oldMat); + rect.RoundOut(); + IntRect surfRect; + ToRect(rect).ToIntRect(&surfRect); + + if (!surfRect.IsEmpty()) { + RefPtr<DrawTarget> 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(oldMat); + + 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 = ToMatrix(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(ThebesMatrix(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) +{ + 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() +{ + mInTransaction = true; + mUsingDefaultTarget = true; + return BeginTransactionWithTarget(mDefaultTarget); +} + +bool +BasicLayerManager::BeginTransactionWithTarget(gfxContext* aTarget) +{ + 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) +{ + PROFILER_LABEL("BasicLayerManager", "EndTransactionInternal", + js::ProfileEntry::Category::GRAPHICS); + +#ifdef MOZ_LAYERS_HAVE_LOG + MOZ_LAYERS_LOG((" ----- (beginning paint)")); + Log(); +#endif + + NS_ASSERTION(InConstruction(), "Should be in construction phase"); + mPhase = PHASE_DRAWING; + + RenderTraceLayers(mRoot, "FF00"); + + mTransactionIncomplete = false; + + if (mRoot) { + if (aFlags & END_NO_COMPOSITE) { + // Apply pending tree updates before recomputing effective + // properties. + 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(ToMatrix(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; + + { + gfxContextMatrixAutoSaveRestore save(mTarget); + mTarget->SetMatrix(gfxMatrix()); + clipRect = ToOutsideIntRect(mTarget->GetClipExtents()); + } + + 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(); + PostPresent(); + + if (!mTransactionIncomplete) { + // Clear out target if we have a complete transaction. + mTarget = nullptr; + } + } + + if (mRoot) { + mAnimationReadyTime = TimeStamp::Now(); + mRoot->StartPendingAnimations(mAnimationReadyTime); + } + +#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 (gfxPrefs::WidgetUpdateFlashing()) { + float r = float(rand()) / RAND_MAX; + float g = float(rand()) / RAND_MAX; + float b = float(rand()) / RAND_MAX; + aContext->SetColor(Color(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); + AutoTArray<Layer*, 12> children; + container->SortChildrenBy3DZOrder(children); + for (uint32_t i = 0; i < children.Length(); i++) { + Layer* layer = children.ElementAt(i); + 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!"; + } + gfxMatrix oldTransform = aTarget->CurrentMatrix(); + transform *= ToMatrix(oldTransform); + aTarget->SetMatrix(ThebesMatrix(transform)); + + aTarget->NewPath(); + aTarget->SnappedRectangle(gfxRect(clipRect->x, clipRect->y, + clipRect->width, clipRect->height)); + aTarget->Clip(); + + aTarget->SetMatrix(oldTransform); +} + +void +BasicLayerManager::PaintLayer(gfxContext* aTarget, + Layer* aLayer, + DrawPaintedLayerCallback aCallback, + void* aCallbackData) +{ + MOZ_ASSERT(aTarget); + + PROFILER_LABEL("BasicLayerManager", "PaintLayer", + js::ProfileEntry::Category::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) { + Color 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->NewPath(); + aTarget->SnappedRectangle(ThebesRect(xformBounds)); + aTarget->Clip(); + 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 000000000..beb4357bb --- /dev/null +++ b/gfx/layers/basic/BasicLayers.h @@ -0,0 +1,217 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef GFX_BASICLAYERS_H +#define GFX_BASICLAYERS_H + +#include <stdint.h> // for INT32_MAX, int32_t +#include "Layers.h" // for Layer (ptr only), etc +#include "gfxTypes.h" +#include "gfxContext.h" // for gfxContext +#include "mozilla/Attributes.h" // for override +#include "mozilla/WidgetUtils.h" // for ScreenRotation +#include "mozilla/layers/LayersTypes.h" // for BufferMode, LayersBackend, etc +#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 ImageFactory; +class ImageLayer; +class PaintLayerContext; +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() override; + virtual bool BeginTransactionWithTarget(gfxContext* aTarget) override; + virtual bool EndEmptyTransaction(EndTransactionFlags aFlags = END_DEFAULT) override; + virtual void EndTransaction(DrawPaintedLayerCallback aCallback, + void* aCallbackData, + EndTransactionFlags aFlags = END_DEFAULT) override; + virtual bool ShouldAvoidComponentAlphaLayers() override { return IsWidgetLayerManager(); } + + 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; } + +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); + + // 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; +}; + +} // 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 000000000..c2262c512 --- /dev/null +++ b/gfx/layers/basic/BasicLayersImpl.cpp @@ -0,0 +1,211 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "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(ThebesMatrix(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 Color& 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 Color& 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); +} + +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(); +} + +ShadowableLayer* +ToShadowable(Layer* aLayer) +{ + return aLayer->AsShadowableLayer(); +} + +bool +ShouldShadow(Layer* aLayer) +{ + if (!ToShadowable(aLayer)) { + MOZ_ASSERT(aLayer->GetType() == Layer::TYPE_READBACK, + "Only expect not to shadow ReadbackLayers"); + return false; + } + return true; +} + + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/basic/BasicLayersImpl.h b/gfx/layers/basic/BasicLayersImpl.h new file mode 100644 index 000000000..5626fe329 --- /dev/null +++ b/gfx/layers/basic/BasicLayersImpl.h @@ -0,0 +1,151 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#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: + virtual ~BasicReadbackLayer() + { + MOZ_COUNT_DTOR(BasicReadbackLayer); + } + +public: + virtual void SetVisibleRegion(const LayerIntRegion& aRegion) + { + 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::Color& 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::Color& aColor, + const gfx::DrawOptions& aOptions, + Layer* aMaskLayer); + +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 000000000..7ce0c24af --- /dev/null +++ b/gfx/layers/basic/BasicPaintedLayer.cpp @@ -0,0 +1,245 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "BasicPaintedLayer.h" +#include <stdint.h> // for uint32_t +#include "GeckoProfiler.h" // for 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) +{ + PROFILER_LABEL("BasicPaintedLayer", "PaintThebes", + js::ProfileEntry::Category::GRAPHICS); + + NS_ASSERTION(BasicManager()->InDrawing(), + "Can only draw in drawing phase"); + + float opacity = GetEffectiveOpacity(); + CompositionOp effectiveOperator = GetEffectiveOperator(this); + + if (!BasicManager()->IsRetained()) { + mValidRegion.SetEmpty(); + 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) { + SetAntialiasingFlags(this, context->GetDrawTarget()); + aCallback(this, context, toDraw, toDraw, DrawRegionClip::NONE, + nsIntRegion(), aCallbackData); + } + 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 |= RotatedContentBuffer::PAINT_WILL_RESAMPLE; + } + if (!(flags & RotatedContentBuffer::PAINT_WILL_RESAMPLE)) { + if (MayResample()) { + flags |= RotatedContentBuffer::PAINT_WILL_RESAMPLE; + } + } +#endif + if (mDrawAtomically) { + flags |= RotatedContentBuffer::PAINT_NO_ROTATION; + } + PaintState state = + mContentClient->BeginPaintBuffer(this, flags); + mValidRegion.Sub(mValidRegion, 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.mDidSelfCopy, + state.mClip, + aCallback, aCallbackData); + MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) PaintThebes", this)); + Mutated(); + ctx = nullptr; + mContentClient->ReturnDrawTargetToBuffer(target); + target = nullptr; + + RenderTraceInvalidateEnd(this, "FFFF00"); + } else { + if (target) { + mContentClient->ReturnDrawTargetToBuffer(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 000000000..e616948e1 --- /dev/null +++ b/gfx/layers/basic/BasicPaintedLayer.h @@ -0,0 +1,133 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef GFX_BASICPAINTEDLAYER_H +#define GFX_BASICPAINTEDLAYER_H + +#include "Layers.h" // for PaintedLayer, LayerManager, etc +#include "RotatedBuffer.h" // for RotatedContentBuffer, 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 RotatedContentBuffer::PaintState PaintState; + typedef RotatedContentBuffer::ContentType ContentType; + + explicit BasicPaintedLayer(BasicLayerManager* aLayerManager, gfx::BackendType aBackend) : + PaintedLayer(aLayerManager, static_cast<BasicImplData*>(this)), + mContentClient(nullptr) + , mBackend(aBackend) + { + MOZ_COUNT_CTOR(BasicPaintedLayer); + } + +protected: + virtual ~BasicPaintedLayer() + { + MOZ_COUNT_DTOR(BasicPaintedLayer); + } + +public: + virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override + { + NS_ASSERTION(BasicManager()->InConstruction(), + "Can only set properties in construction phase"); + PaintedLayer::SetVisibleRegion(aRegion); + } + virtual void InvalidateRegion(const nsIntRegion& aRegion) override + { + NS_ASSERTION(BasicManager()->InConstruction(), + "Can only set properties in construction phase"); + mInvalidRegion.Add(aRegion); + mValidRegion.Sub(mValidRegion, mInvalidRegion.GetRegion()); + } + + virtual void PaintThebes(gfxContext* aContext, + Layer* aMaskLayer, + LayerManager::DrawPaintedLayerCallback aCallback, + void* aCallbackData) override; + + virtual void Validate(LayerManager::DrawPaintedLayerCallback aCallback, + void* aCallbackData, + ReadbackProcessor* aReadback) override; + + virtual void ClearCachedResources() override + { + if (mContentClient) { + mContentClient->Clear(); + } + mValidRegion.SetEmpty(); + } + + virtual 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); + mValidRegion.SetEmpty(); + } + 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, + bool aDidSelfCopy, + 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); + mValidRegion.Or(mValidRegion, 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 000000000..8834773e4 --- /dev/null +++ b/gfx/layers/basic/MacIOSurfaceTextureHostBasic.cpp @@ -0,0 +1,107 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "MacIOSurfaceTextureHostBasic.h" +#include "mozilla/gfx/MacIOSurface.h" +#include "MacIOSurfaceHelpers.h" + +namespace mozilla { +namespace layers { + +MacIOSurfaceTextureSourceBasic::MacIOSurfaceTextureSourceBasic( + BasicCompositor* aCompositor, + MacIOSurface* aSurface) + : mCompositor(aCompositor) + , 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()); +} + +gfx::SourceSurface* +MacIOSurfaceTextureSourceBasic::GetSurface(gfx::DrawTarget* aTarget) +{ + if (!mSourceSurface) { + mSourceSurface = CreateSourceSurfaceFromMacIOSurface(mSurface); + } + return mSourceSurface; +} + +void +MacIOSurfaceTextureSourceBasic::SetCompositor(Compositor* aCompositor) +{ + mCompositor = AssertBasicCompositor(aCompositor); +} + +bool +MacIOSurfaceTextureHostBasic::Lock() +{ + if (!mCompositor) { + return false; + } + + if (!mTextureSource) { + mTextureSource = new MacIOSurfaceTextureSourceBasic(mCompositor, mSurface); + } + return true; +} + +void +MacIOSurfaceTextureHostBasic::SetCompositor(Compositor* aCompositor) +{ + BasicCompositor* compositor = AssertBasicCompositor(aCompositor); + if (!compositor) { + mTextureSource = nullptr; + return; + } + mCompositor = compositor; + if (mTextureSource) { + mTextureSource->SetCompositor(compositor); + } +} + +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 000000000..7949aecfc --- /dev/null +++ b/gfx/layers/basic/MacIOSurfaceTextureHostBasic.h @@ -0,0 +1,97 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#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: + MacIOSurfaceTextureSourceBasic(BasicCompositor* aCompositor, + MacIOSurface* aSurface); + virtual ~MacIOSurfaceTextureSourceBasic(); + + virtual const char* Name() const override { return "MacIOSurfaceTextureSourceBasic"; } + + virtual TextureSourceBasic* 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 SetCompositor(Compositor* aCompositor) override; + +protected: + RefPtr<BasicCompositor> mCompositor; + 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); + + virtual void SetCompositor(Compositor* aCompositor) override; + + virtual Compositor* GetCompositor() override { return mCompositor; } + + virtual bool Lock() override; + + virtual gfx::SurfaceFormat GetFormat() const override; + + virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override + { + aTexture = mTextureSource; + return !!aTexture; + } + + virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override + { + return nullptr; // XXX - implement this (for MOZ_DUMP_PAINTING) + } + + virtual gfx::IntSize GetSize() const override; + +#ifdef MOZ_LAYERS_HAVE_LOG + virtual const char* Name() override { return "MacIOSurfaceTextureHostBasic"; } +#endif + +protected: + RefPtr<BasicCompositor> mCompositor; + 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 000000000..c79ab1a53 --- /dev/null +++ b/gfx/layers/basic/TextureClientX11.cpp @@ -0,0 +1,156 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "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/gfx/2D.h" +#include "mozilla/gfx/Logging.h" +#include "gfxXlibSurface.h" +#include "gfx2DGlue.h" + +#include "mozilla/X11Util.h" +#include <X11/Xlib.h> + +using namespace mozilla; +using namespace mozilla::gfx; + +namespace mozilla { +namespace 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 +} // namespace diff --git a/gfx/layers/basic/TextureClientX11.h b/gfx/layers/basic/TextureClientX11.h new file mode 100644 index 000000000..084538ea1 --- /dev/null +++ b/gfx/layers/basic/TextureClientX11.h @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_GFX_TEXTURECLIENT_X11_H +#define MOZILLA_GFX_TEXTURECLIENT_X11_H + +#include "mozilla/layers/TextureClient.h" +#include "ISurfaceAllocator.h" // For IsSurfaceDescriptorValid +#include "mozilla/layers/ShadowLayerUtilsX11.h" + +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 000000000..8058e43ef --- /dev/null +++ b/gfx/layers/basic/TextureHostBasic.cpp @@ -0,0 +1,33 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- +* This Source Code Form is subject to the terms of the Mozilla Public +* License, v. 2.0. If a copy of the MPL was not distributed with this +* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "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, + 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, aFlags); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/basic/TextureHostBasic.h b/gfx/layers/basic/TextureHostBasic.h new file mode 100644 index 000000000..e08624f5a --- /dev/null +++ b/gfx/layers/basic/TextureHostBasic.h @@ -0,0 +1,34 @@ + +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- +* This Source Code Form is subject to the terms of the Mozilla Public +* License, v. 2.0. If a copy of the MPL was not distributed with this +* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#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 { + +/** + * A texture source interface that can be used by the software Compositor. + */ +class TextureSourceBasic +{ +public: + TextureSourceBasic() : mFromYCBCR(false) {} + virtual ~TextureSourceBasic() {} + 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 000000000..6f21d15d2 --- /dev/null +++ b/gfx/layers/basic/X11BasicCompositor.cpp @@ -0,0 +1,136 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- +* This Source Code Form is subject to the terms of the Mozilla Public +* License, v. 2.0. If a copy of the MPL was not distributed with this +* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "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::GetPlatform()-> + 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); + } else { + 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; + } else { + return mBufferDrawTarget->GetFormat(); + } +} + +SourceSurface* +X11DataTextureSourceBasic::GetSurface(DrawTarget* aTarget) +{ + RefPtr<gfx::SourceSurface> surface; + if (mBufferDrawTarget) { + surface = mBufferDrawTarget->Snapshot(); + return surface.get(); + } else { + 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 000000000..2504e3f67 --- /dev/null +++ b/gfx/layers/basic/X11BasicCompositor.h @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- +* This Source Code Form is subject to the terms of the Mozilla Public +* License, v. 2.0. If a copy of the MPL was not distributed with this +* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#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() {}; + + virtual const char* Name() const override { return "X11DataTextureSourceBasic"; } + + virtual bool Update(gfx::DataSourceSurface* aSurface, + nsIntRegion* aDestRegion = nullptr, + gfx::IntPoint* aSrcOffset = nullptr) override; + + virtual TextureSourceBasic* AsSourceBasic() override; + + virtual gfx::SourceSurface* GetSurface(gfx::DrawTarget* aTarget) override; + + virtual void DeallocateDeviceData() override; + + virtual gfx::IntSize GetSize() const override; + + virtual 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) + {} + + virtual already_AddRefed<DataTextureSource> + CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) override; + + virtual already_AddRefed<DataTextureSource> + CreateDataTextureSourceAround(gfx::DataSourceSurface* aSurface) override { return nullptr; } + + virtual 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 000000000..24f21e5fc --- /dev/null +++ b/gfx/layers/basic/X11TextureSourceBasic.cpp @@ -0,0 +1,67 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "X11TextureSourceBasic.h" +#include "gfxXlibSurface.h" +#include "gfx2DGlue.h" + +namespace mozilla { +namespace layers { + +using namespace mozilla::gfx; + +X11TextureSourceBasic::X11TextureSourceBasic(BasicCompositor* aCompositor, gfxXlibSurface* aSurface) + : mCompositor(aCompositor), + 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; +} + +void +X11TextureSourceBasic::SetCompositor(Compositor* aCompositor) +{ + mCompositor = AssertBasicCompositor(aCompositor); +} + +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; + } +} + +} +} diff --git a/gfx/layers/basic/X11TextureSourceBasic.h b/gfx/layers/basic/X11TextureSourceBasic.h new file mode 100644 index 000000000..f813560e0 --- /dev/null +++ b/gfx/layers/basic/X11TextureSourceBasic.h @@ -0,0 +1,54 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#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 SetCompositor(Compositor* aCompositor) override; + + virtual void Updated() override { } + + static gfx::SurfaceFormat ContentTypeToSurfaceFormat(gfxContentType aType); + +protected: + RefPtr<BasicCompositor> mCompositor; + RefPtr<gfxXlibSurface> mSurface; + RefPtr<gfx::SourceSurface> mSourceSurface; +}; + +} // namespace layers +} // namespace mozilla + +#endif // MOZILLA_GFX_X11TEXTURESOURCEBASIC__H |