diff -r 000000000000 -r 6474c204b198 gfx/layers/basic/BasicCompositor.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gfx/layers/basic/BasicCompositor.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,495 @@ +/* -*- 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 "mozilla/layers/YCbCrImageDataSerializer.h" +#include "nsIWidget.h" +#include "gfx2DGlue.h" +#include "mozilla/gfx/2D.h" +#include "mozilla/gfx/Helpers.h" +#include "gfxUtils.h" +#include "YCbCrUtils.h" +#include +#include "ImageContainer.h" +#include "gfxPrefs.h" +#define PIXMAN_DONT_DEFINE_STDINT +#include "pixman.h" // for pixman_f_transform, etc + +namespace mozilla { +using namespace mozilla::gfx; + +namespace layers { + +class DataTextureSourceBasic : public DataTextureSource + , public TextureSourceBasic +{ +public: + + virtual TextureSourceBasic* AsSourceBasic() MOZ_OVERRIDE { return this; } + + virtual gfx::SourceSurface* GetSurface(DrawTarget* aTarget) MOZ_OVERRIDE { return mSurface; } + + SurfaceFormat GetFormat() const MOZ_OVERRIDE + { + return mSurface->GetFormat(); + } + + virtual IntSize GetSize() const MOZ_OVERRIDE + { + return mSurface->GetSize(); + } + + virtual bool Update(gfx::DataSourceSurface* aSurface, + nsIntRegion* aDestRegion = nullptr, + gfx::IntPoint* aSrcOffset = nullptr) MOZ_OVERRIDE + { + // XXX - For this to work with IncrementalContentHost we will need to support + // the aDestRegion and aSrcOffset parameters properly; + mSurface = aSurface; + return true; + } + + virtual void DeallocateDeviceData() MOZ_OVERRIDE + { + mSurface = nullptr; + SetUpdateSerial(0); + } + +public: + RefPtr mSurface; +}; + +BasicCompositor::BasicCompositor(nsIWidget *aWidget) + : mWidget(aWidget) +{ + MOZ_COUNT_CTOR(BasicCompositor); + SetBackend(LayersBackend::LAYERS_BASIC); +} + +BasicCompositor::~BasicCompositor() +{ + MOZ_COUNT_DTOR(BasicCompositor); +} + +void BasicCompositor::Destroy() +{ + mWidget->CleanupRemoteDrawing(); + mWidget = nullptr; +} + +TemporaryRef +BasicCompositor::CreateRenderTarget(const IntRect& aRect, SurfaceInitMode aInit) +{ + RefPtr target = mDrawTarget->CreateSimilarDrawTarget(aRect.Size(), SurfaceFormat::B8G8R8A8); + + RefPtr rt = new BasicCompositingRenderTarget(target, aRect); + + return rt.forget(); +} + +TemporaryRef +BasicCompositor::CreateRenderTargetFromSource(const IntRect &aRect, + const CompositingRenderTarget *aSource, + const IntPoint &aSourcePoint) +{ + RefPtr target = mDrawTarget->CreateSimilarDrawTarget(aRect.Size(), SurfaceFormat::B8G8R8A8); + RefPtr rt = new BasicCompositingRenderTarget(target, aRect); + + DrawTarget *source; + if (aSource) { + const BasicCompositingRenderTarget* sourceSurface = + static_cast(aSource); + source = sourceSurface->mDrawTarget; + } else { + source = mDrawTarget; + } + + RefPtr snapshot = source->Snapshot(); + + IntRect sourceRect(aSourcePoint, aRect.Size()); + rt->mDrawTarget->CopySurface(snapshot, sourceRect, IntPoint(0, 0)); + return rt.forget(); +} + +TemporaryRef +BasicCompositor::CreateDataTextureSource(TextureFlags aFlags) +{ + RefPtr result = new DataTextureSourceBasic(); + return result.forget(); +} + +bool +BasicCompositor::SupportsEffect(EffectTypes aEffect) +{ + return static_cast(aEffect) != EFFECT_YCBCR; +} + +static void +DrawSurfaceWithTextureCoords(DrawTarget *aDest, + const gfx::Rect& aDestRect, + SourceSurface *aSource, + const gfx::Rect& aTextureCoords, + gfx::Filter aFilter, + float aOpacity, + SourceSurface *aMask, + const Matrix* aMaskTransform) +{ + // 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); + // Compute a transform that maps sourceRect to aDestRect. + gfxMatrix transform = + gfxUtils::TransformRectToRect(sourceRect, + gfxPoint(aDestRect.x, aDestRect.y), + gfxPoint(aDestRect.XMost(), aDestRect.y), + gfxPoint(aDestRect.XMost(), aDestRect.YMost())); + Matrix matrix = ToMatrix(transform); + + // 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, aFilter, DrawOptions(aOpacity), + mode, aMask, aMaskTransform, &matrix); +} + +static pixman_transform +Matrix3DToPixman(const gfx3DMatrix& aMatrix) +{ + pixman_f_transform transform; + + transform.m[0][0] = aMatrix._11; + transform.m[0][1] = aMatrix._21; + transform.m[0][2] = aMatrix._41; + transform.m[1][0] = aMatrix._12; + transform.m[1][1] = aMatrix._22; + transform.m[1][2] = aMatrix._42; + transform.m[2][0] = aMatrix._14; + transform.m[2][1] = aMatrix._24; + transform.m[2][2] = aMatrix._44; + + pixman_transform result; + pixman_transform_from_pixman_f_transform(&result, &transform); + + return result; +} + +static void +PixmanTransform(DataSourceSurface* aDest, + DataSourceSurface* aSource, + const gfx3DMatrix& aTransform, + gfxPoint aDestOffset) +{ + IntSize destSize = aDest->GetSize(); + pixman_image_t* dest = pixman_image_create_bits(PIXMAN_a8r8g8b8, + destSize.width, + destSize.height, + (uint32_t*)aDest->GetData(), + aDest->Stride()); + + IntSize srcSize = aSource->GetSize(); + pixman_image_t* src = pixman_image_create_bits(PIXMAN_a8r8g8b8, + srcSize.width, + srcSize.height, + (uint32_t*)aSource->GetData(), + aSource->Stride()); + + NS_ABORT_IF_FALSE(src && dest, "Failed to create pixman images?"); + + pixman_transform pixTransform = Matrix3DToPixman(aTransform); + pixman_transform pixTransformInverted; + + // If the transform is singular then nothing would be drawn anyway, return here + if (!pixman_transform_invert(&pixTransformInverted, &pixTransform)) { + pixman_image_unref(dest); + pixman_image_unref(src); + return; + } + pixman_image_set_transform(src, &pixTransformInverted); + + pixman_image_composite32(PIXMAN_OP_SRC, + src, + nullptr, + dest, + aDestOffset.x, + aDestOffset.y, + 0, + 0, + 0, + 0, + destSize.width, + destSize.height); + + pixman_image_unref(dest); + pixman_image_unref(src); +} + +static inline IntRect +RoundOut(Rect r) +{ + r.RoundOut(); + return IntRect(r.x, r.y, r.width, r.height); +} + +void +BasicCompositor::DrawQuad(const gfx::Rect& aRect, + const gfx::Rect& aClipRect, + const EffectChain &aEffectChain, + gfx::Float aOpacity, + const gfx::Matrix4x4 &aTransform) +{ + RefPtr buffer = mRenderTarget->mDrawTarget; + + // For 2D drawing, |dest| and |buffer| are the same surface. For 3D drawing, + // |dest| is a temporary surface. + RefPtr dest = buffer; + + buffer->PushClipRect(aClipRect); + AutoSaveTransform autoSaveTransform(dest); + + Matrix newTransform; + Rect transformBounds; + gfx3DMatrix new3DTransform; + IntPoint offset = mRenderTarget->GetOrigin(); + + if (aTransform.Is2D()) { + newTransform = aTransform.As2D(); + } else { + // Create a temporary surface for the transform. + dest = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(RoundOut(aRect).Size(), SurfaceFormat::B8G8R8A8); + if (!dest) { + return; + } + + // Get the bounds post-transform. + To3DMatrix(aTransform, new3DTransform); + gfxRect bounds = new3DTransform.TransformBounds(ThebesRect(aRect)); + bounds.IntersectRect(bounds, gfxRect(offset.x, offset.y, buffer->GetSize().width, buffer->GetSize().height)); + + transformBounds = ToRect(bounds); + transformBounds.RoundOut(); + + // Propagate the coordinate offset to our 2D draw target. + newTransform.Translate(transformBounds.x, transformBounds.y); + + // When we apply the 3D transformation, we do it against a temporary + // surface, so undo the coordinate offset. + new3DTransform = new3DTransform * gfx3DMatrix::Translation(-transformBounds.x, -transformBounds.y, 0); + + transformBounds.MoveTo(0, 0); + } + + newTransform.PostTranslate(-offset.x, -offset.y); + buffer->SetTransform(newTransform); + + RefPtr sourceMask; + Matrix maskTransform; + if (aEffectChain.mSecondaryEffects[EFFECT_MASK]) { + EffectMask *effectMask = static_cast(aEffectChain.mSecondaryEffects[EFFECT_MASK].get()); + sourceMask = effectMask->mMaskTexture->AsSourceBasic()->GetSurface(dest); + MOZ_ASSERT(effectMask->mMaskTransform.Is2D(), "How did we end up with a 3D transform here?!"); + MOZ_ASSERT(!effectMask->mIs3D); + maskTransform = effectMask->mMaskTransform.As2D(); + maskTransform.Translate(-offset.x, -offset.y); + } + + switch (aEffectChain.mPrimaryEffect->mType) { + case EFFECT_SOLID_COLOR: { + EffectSolidColor* effectSolidColor = + static_cast(aEffectChain.mPrimaryEffect.get()); + + FillRectWithMask(dest, aRect, effectSolidColor->mColor, + DrawOptions(aOpacity), sourceMask, &maskTransform); + break; + } + case EFFECT_RGB: { + TexturedEffect* texturedEffect = + static_cast(aEffectChain.mPrimaryEffect.get()); + TextureSourceBasic* source = texturedEffect->mTexture->AsSourceBasic(); + + DrawSurfaceWithTextureCoords(dest, aRect, + source->GetSurface(dest), + texturedEffect->mTextureCoords, + texturedEffect->mFilter, + aOpacity, sourceMask, &maskTransform); + break; + } + case EFFECT_YCBCR: { + NS_RUNTIMEABORT("Can't (easily) support component alpha with BasicCompositor!"); + break; + } + case EFFECT_RENDER_TARGET: { + EffectRenderTarget* effectRenderTarget = + static_cast(aEffectChain.mPrimaryEffect.get()); + RefPtr surface + = static_cast(effectRenderTarget->mRenderTarget.get()); + RefPtr sourceSurf = surface->mDrawTarget->Snapshot(); + + DrawSurfaceWithTextureCoords(dest, aRect, + sourceSurf, + effectRenderTarget->mTextureCoords, + effectRenderTarget->mFilter, + aOpacity, sourceMask, &maskTransform); + break; + } + case EFFECT_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 snapshot = dest->Snapshot(); + RefPtr source = snapshot->GetDataSurface(); + RefPtr temp = + Factory::CreateDataSourceSurface(RoundOut(transformBounds).Size(), SurfaceFormat::B8G8R8A8); + if (!temp) { + return; + } + + PixmanTransform(temp, source, new3DTransform, gfxPoint(0, 0)); + + buffer->DrawSurface(temp, transformBounds, transformBounds); + } + + buffer->PopClip(); +} + +void +BasicCompositor::BeginFrame(const nsIntRegion& aInvalidRegion, + const gfx::Rect *aClipRectIn, + const gfx::Matrix& aTransform, + const gfx::Rect& aRenderBounds, + gfx::Rect *aClipRectOut /* = nullptr */, + gfx::Rect *aRenderBoundsOut /* = nullptr */) +{ + nsIntRect intRect; + mWidget->GetClientBounds(intRect); + mWidgetSize = gfx::ToIntSize(intRect.Size()); + + // The result of GetClientBounds is shifted over by the size of the window + // manager styling. We want to ignore that. + intRect.MoveTo(0, 0); + Rect rect = Rect(0, 0, intRect.width, intRect.height); + + // Sometimes the invalid region is larger than we want to draw. + nsIntRegion invalidRegionSafe; + invalidRegionSafe.And(aInvalidRegion, intRect); + + // FIXME: Redraw the whole screen in every frame to work around bug 972728. + invalidRegionSafe = intRect; + + nsIntRect invalidRect = invalidRegionSafe.GetBounds(); + mInvalidRect = IntRect(invalidRect.x, invalidRect.y, invalidRect.width, invalidRect.height); + mInvalidRegion = invalidRegionSafe; + + if (aRenderBoundsOut) { + *aRenderBoundsOut = Rect(); + } + + if (mInvalidRect.width <= 0 || mInvalidRect.height <= 0) { + return; + } + + if (mCopyTarget) { + // If we have a copy target, then we don't have a widget-provided mDrawTarget (currently). Create a dummy + // placeholder so that CreateRenderTarget() works. + mDrawTarget = gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget(IntSize(1,1), SurfaceFormat::B8G8R8A8); + } else { + mDrawTarget = mWidget->StartRemoteDrawing(); + } + if (!mDrawTarget) { + return; + } + + // Setup an intermediate render target to buffer all compositing. We will + // copy this into mDrawTarget (the widget), and/or mCopyTarget in EndFrame() + RefPtr target = CreateRenderTarget(mInvalidRect, INIT_MODE_CLEAR); + SetRenderTarget(target); + + // We only allocate a surface sized to the invalidated region, so we need to + // translate future coordinates. + Matrix transform; + transform.Translate(-invalidRect.x, -invalidRect.y); + mRenderTarget->mDrawTarget->SetTransform(transform); + + gfxUtils::ClipToRegion(mRenderTarget->mDrawTarget, invalidRegionSafe); + + if (aRenderBoundsOut) { + *aRenderBoundsOut = rect; + } + + if (aClipRectIn) { + mRenderTarget->mDrawTarget->PushClipRect(*aClipRectIn); + } else { + mRenderTarget->mDrawTarget->PushClipRect(rect); + if (aClipRectOut) { + *aClipRectOut = rect; + } + } +} + +void +BasicCompositor::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(ToRect(mInvalidRegion.GetBounds()), + ColorPattern(Color(r, g, b, 0.2f))); + } + + // Pop aInvalidregion + mRenderTarget->mDrawTarget->PopClip(); + + // Note: Most platforms require us to buffer drawing to the widget surface. + // That's why we don't draw to mDrawTarget directly. + RefPtr source = mRenderTarget->mDrawTarget->Snapshot(); + RefPtr dest(mCopyTarget ? mCopyTarget : mDrawTarget); + + // 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. + nsIntRegionRectIterator iter(mInvalidRegion); + for (const nsIntRect *r = iter.Next(); r; r = iter.Next()) { + dest->CopySurface(source, + IntRect(r->x - mInvalidRect.x, r->y - mInvalidRect.y, r->width, r->height), + IntPoint(r->x, r->y)); + } + if (!mCopyTarget) { + mWidget->EndRemoteDrawing(); + } + + mDrawTarget = nullptr; + mRenderTarget = nullptr; +} + +void +BasicCompositor::AbortFrame() +{ + mRenderTarget->mDrawTarget->PopClip(); + mRenderTarget->mDrawTarget->PopClip(); + mDrawTarget = nullptr; + mRenderTarget = nullptr; +} + +} +}