forked from mirrors/gecko-dev
		
	 b2d4c86823
			
		
	
	
		b2d4c86823
		
	
	
	
	
		
			
			--HG-- extra : rebase_source : 56f2cc017632bf27115490ae05254019108c6179 extra : amend_source : 98ea6c3c02a9f7650d2cf65deaf5085cf9a2efa4
		
			
				
	
	
		
			1059 lines
		
	
	
	
		
			30 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1059 lines
		
	
	
	
		
			30 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #include "RenderPassMLGPU.h"
 | |
| #include "ContainerLayerMLGPU.h"
 | |
| #include "FrameBuilder.h"
 | |
| #include "ImageLayerMLGPU.h"
 | |
| #include "LayersLogging.h"
 | |
| #include "MaskOperation.h"
 | |
| #include "MLGDevice.h"
 | |
| #include "PaintedLayerMLGPU.h"
 | |
| #include "RenderViewMLGPU.h"
 | |
| #include "ShaderDefinitionsMLGPU.h"
 | |
| #include "ShaderDefinitionsMLGPU-inl.h"
 | |
| #include "SharedBufferMLGPU.h"
 | |
| #include "mozilla/layers/LayersHelpers.h"
 | |
| #include "mozilla/layers/LayersMessages.h"
 | |
| #include "RenderPassMLGPU-inl.h"
 | |
| 
 | |
| namespace mozilla {
 | |
| namespace layers {
 | |
| 
 | |
| using namespace gfx;
 | |
| 
 | |
| ItemInfo::ItemInfo(FrameBuilder* aBuilder,
 | |
|                    RenderViewMLGPU* aView,
 | |
|                    LayerMLGPU* aLayer,
 | |
|                    int32_t aSortOrder,
 | |
|                    const IntRect& aBounds,
 | |
|                    Maybe<Polygon>&& aGeometry)
 | |
|  : view(aView),
 | |
|    layer(aLayer),
 | |
|    type(RenderPassType::Unknown),
 | |
|    layerIndex(kInvalidResourceIndex),
 | |
|    sortOrder(aSortOrder),
 | |
|    bounds(aBounds),
 | |
|    geometry(std::move(aGeometry))
 | |
| {
 | |
|   const Matrix4x4& transform = aLayer->GetLayer()->GetEffectiveTransform();
 | |
| 
 | |
|   Matrix transform2D;
 | |
|   if (!geometry &&
 | |
|       transform.Is2D(&transform2D) &&
 | |
|       transform2D.IsRectilinear())
 | |
|   {
 | |
|     this->rectilinear = true;
 | |
|     if (transform2D.IsIntegerTranslation()) {
 | |
|       this->translation = Some(IntPoint::Truncate(transform2D.GetTranslation()));
 | |
|     }
 | |
|   } else {
 | |
|     this->rectilinear = false;
 | |
|   }
 | |
| 
 | |
|   // Layers can have arbitrary clips or transforms, and we can't use built-in
 | |
|   // scissor functionality when batching. Instead, pixel shaders will write
 | |
|   // transparent pixels for positions outside of the clip. Unfortunately that
 | |
|   // breaks z-buffering because the transparent pixels will still write to
 | |
|   // the depth buffer.
 | |
|   //
 | |
|   // To make this work, we clamp the final vertices in the vertex shader to
 | |
|   // the clip rect. We can only do this for rectilinear transforms. If a
 | |
|   // transform can produce a rotation or perspective change, then we might
 | |
|   // accidentally change the geometry. These items are not treated as
 | |
|   // opaque.
 | |
|   //
 | |
|   // Also, we someday want non-rectilinear items to be antialiased with DEAA,
 | |
|   // and we can't do this if the items are rendered front-to-back, since
 | |
|   // such items cannot be blended. (Though we could consider adding these
 | |
|   // items in two separate draw calls, one for DEAA and for not - that is
 | |
|   // definitely future work.)
 | |
|   if (aLayer->GetComputedOpacity() != 1.0f ||
 | |
|       aLayer->GetMask() ||
 | |
|       !aLayer->IsContentOpaque() ||
 | |
|       !rectilinear)
 | |
|   {
 | |
|     this->opaque = false;
 | |
|     this->renderOrder = RenderOrder::BackToFront;
 | |
|   } else {
 | |
|     this->opaque = true;
 | |
|     this->renderOrder = aView->HasDepthBuffer()
 | |
|                         ? RenderOrder::FrontToBack
 | |
|                         : RenderOrder::BackToFront;
 | |
|   }
 | |
| 
 | |
|   this->type = RenderPassMLGPU::GetPreferredPassType(aBuilder, *this);
 | |
| }
 | |
| 
 | |
| RenderPassType
 | |
| RenderPassMLGPU::GetPreferredPassType(FrameBuilder* aBuilder, const ItemInfo& aItem)
 | |
| {
 | |
|   LayerMLGPU* layer = aItem.layer;
 | |
|   switch (layer->GetType()) {
 | |
|   case Layer::TYPE_COLOR:
 | |
|   {
 | |
|     if (aBuilder->GetDevice()->CanUseClearView() &&
 | |
|         aItem.HasRectTransformAndClip() &&
 | |
|         aItem.translation &&
 | |
|         aItem.opaque &&
 | |
|         !aItem.view->HasDepthBuffer())
 | |
|     {
 | |
|       // Note: we don't have ClearView set up to do depth buffer writes, so we
 | |
|       // exclude depth buffering from the test above.
 | |
|       return RenderPassType::ClearView;
 | |
|     }
 | |
|     return RenderPassType::SolidColor;
 | |
|   }
 | |
|   case Layer::TYPE_PAINTED: {
 | |
|     PaintedLayerMLGPU* painted = layer->AsPaintedLayerMLGPU();
 | |
|     if (painted->HasComponentAlpha()) {
 | |
|       return RenderPassType::ComponentAlpha;
 | |
|     }
 | |
|     return RenderPassType::SingleTexture;
 | |
|   }
 | |
|   case Layer::TYPE_CANVAS:
 | |
|     return RenderPassType::SingleTexture;
 | |
|   case Layer::TYPE_IMAGE: {
 | |
|     ImageHost* host = layer->AsTexturedLayerMLGPU()->GetImageHost();
 | |
|     TextureHost* texture = host->CurrentTextureHost();
 | |
|     if (texture->GetReadFormat() == SurfaceFormat::YUV ||
 | |
|         texture->GetReadFormat() == SurfaceFormat::NV12)
 | |
|     {
 | |
|       return RenderPassType::Video;
 | |
|     }
 | |
|     return RenderPassType::SingleTexture;
 | |
|   }
 | |
|   case Layer::TYPE_CONTAINER:
 | |
|     return RenderPassType::RenderView;
 | |
|   default:
 | |
|     return RenderPassType::Unknown;
 | |
|   }
 | |
| }
 | |
| 
 | |
| RefPtr<RenderPassMLGPU>
 | |
| RenderPassMLGPU::CreatePass(FrameBuilder* aBuilder, const ItemInfo& aItem)
 | |
| {
 | |
|   switch (aItem.type) {
 | |
|   case RenderPassType::SolidColor:
 | |
|     return MakeAndAddRef<SolidColorPass>(aBuilder, aItem);
 | |
|   case RenderPassType::SingleTexture:
 | |
|     return MakeAndAddRef<SingleTexturePass>(aBuilder, aItem);
 | |
|   case RenderPassType::RenderView:
 | |
|     return MakeAndAddRef<RenderViewPass>(aBuilder, aItem);
 | |
|   case RenderPassType::Video:
 | |
|     return MakeAndAddRef<VideoRenderPass>(aBuilder, aItem);
 | |
|   case RenderPassType::ComponentAlpha:
 | |
|     return MakeAndAddRef<ComponentAlphaPass>(aBuilder, aItem);
 | |
|   case RenderPassType::ClearView:
 | |
|     return MakeAndAddRef<ClearViewPass>(aBuilder, aItem);
 | |
|   default:
 | |
|     return nullptr;
 | |
|   }
 | |
| }
 | |
| 
 | |
| RenderPassMLGPU::RenderPassMLGPU(FrameBuilder* aBuilder, const ItemInfo& aItem)
 | |
|  : mBuilder(aBuilder),
 | |
|    mDevice(aBuilder->GetDevice()),
 | |
|    mLayerBufferIndex(aBuilder->CurrentLayerBufferIndex()),
 | |
|    mMaskRectBufferIndex(kInvalidResourceIndex),
 | |
|    mPrepared(false)
 | |
| {
 | |
| }
 | |
| 
 | |
| RenderPassMLGPU::~RenderPassMLGPU()
 | |
| {
 | |
| }
 | |
| 
 | |
| bool
 | |
| RenderPassMLGPU::IsCompatible(const ItemInfo& aItem)
 | |
| {
 | |
|   if (GetType() != aItem.type) {
 | |
|     return false;
 | |
|   }
 | |
|   if (mLayerBufferIndex != mBuilder->CurrentLayerBufferIndex()) {
 | |
|     return false;
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool
 | |
| RenderPassMLGPU::AcceptItem(ItemInfo& aInfo)
 | |
| {
 | |
|   MOZ_ASSERT(IsCompatible(aInfo));
 | |
| 
 | |
|   if (!AddToPass(aInfo.layer, aInfo)) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   if (aInfo.renderOrder == RenderOrder::BackToFront) {
 | |
|     mAffectedRegion.OrWith(aInfo.bounds);
 | |
|     mAffectedRegion.SimplifyOutward(4);
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool
 | |
| RenderPassMLGPU::Intersects(const ItemInfo& aItem)
 | |
| {
 | |
|   MOZ_ASSERT(aItem.renderOrder == RenderOrder::BackToFront);
 | |
|   return !mAffectedRegion.Intersect(aItem.bounds).IsEmpty();
 | |
| }
 | |
| 
 | |
| void
 | |
| RenderPassMLGPU::PrepareForRendering()
 | |
| {
 | |
|   mPrepared = true;
 | |
| }
 | |
| 
 | |
| ShaderRenderPass::ShaderRenderPass(FrameBuilder* aBuilder, const ItemInfo& aItem)
 | |
|  : RenderPassMLGPU(aBuilder, aItem),
 | |
|    mGeometry(GeometryMode::Unknown),
 | |
|    mHasRectTransformAndClip(aItem.HasRectTransformAndClip())
 | |
| {
 | |
|   mMask = aItem.layer->GetMask();
 | |
|   if (mMask) {
 | |
|     mMaskRectBufferIndex = mBuilder->CurrentMaskRectBufferIndex();
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool
 | |
| ShaderRenderPass::IsCompatible(const ItemInfo& aItem)
 | |
| {
 | |
|   MOZ_ASSERT(mGeometry != GeometryMode::Unknown);
 | |
| 
 | |
|   if (!RenderPassMLGPU::IsCompatible(aItem)) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   // A masked batch cannot accept non-masked items, since the pixel shader
 | |
|   // bakes in whether a mask is present. Also, the pixel shader can only bind
 | |
|   // one specific mask at a time.
 | |
|   if (aItem.layer->GetMask() != mMask) {
 | |
|     return false;
 | |
|   }
 | |
|   if (mMask && mBuilder->CurrentMaskRectBufferIndex() != mMaskRectBufferIndex) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   // We key batches on this property, since we can use more efficient pixel
 | |
|   // shaders if we don't need to propagate a clip and a mask.
 | |
|   if (mHasRectTransformAndClip != aItem.HasRectTransformAndClip()) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   // We should be assured at this point, that if the item requires complex
 | |
|   // geometry, then it should have already been rejected from a unit-quad
 | |
|   // batch. Therefore this batch should be in polygon mode.
 | |
|   MOZ_ASSERT_IF(aItem.geometry.isSome(), mGeometry == GeometryMode::Polygon);
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| void
 | |
| ShaderRenderPass::SetGeometry(const ItemInfo& aItem, GeometryMode aMode)
 | |
| {
 | |
|   MOZ_ASSERT(mGeometry == GeometryMode::Unknown);
 | |
| 
 | |
|   if (aMode == GeometryMode::Unknown) {
 | |
|     mGeometry = mHasRectTransformAndClip
 | |
|                 ? GeometryMode::UnitQuad
 | |
|                 : GeometryMode::Polygon;
 | |
|   } else {
 | |
|     mGeometry = aMode;
 | |
|   }
 | |
| 
 | |
|   // Since we process layers front-to-back, back-to-front items are
 | |
|   // in the wrong order. We address this by automatically reversing
 | |
|   // the buffers we use to build vertices.
 | |
|   if (aItem.renderOrder != RenderOrder::FrontToBack) {
 | |
|     mInstances.SetReversed();
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| ShaderRenderPass::PrepareForRendering()
 | |
| {
 | |
|   if (mInstances.IsEmpty()) {
 | |
|     return;
 | |
|   }
 | |
|   if (!mDevice->GetSharedVertexBuffer()->Allocate(&mInstanceBuffer, mInstances) ||
 | |
|       !SetupPSBuffer0(GetOpacity()) ||
 | |
|       !OnPrepareBuffers())
 | |
|   {
 | |
|     return;
 | |
|   }
 | |
|   return RenderPassMLGPU::PrepareForRendering();
 | |
| }
 | |
| 
 | |
| bool
 | |
| ShaderRenderPass::SetupPSBuffer0(float aOpacity)
 | |
| {
 | |
|   if (aOpacity == 1.0f && !HasMask()) {
 | |
|     mPSBuffer0 = mBuilder->GetDefaultMaskInfo();
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   MaskInformation cb(aOpacity, HasMask());
 | |
|   return mDevice->GetSharedPSBuffer()->Allocate(&mPSBuffer0, cb);
 | |
| }
 | |
| 
 | |
| void
 | |
| ShaderRenderPass::ExecuteRendering()
 | |
| {
 | |
|   if (mInstances.IsEmpty()) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // Change the blend state if needed.
 | |
|   if (Maybe<MLGBlendState> blendState = GetBlendState()) {
 | |
|     mDevice->SetBlendState(blendState.value());
 | |
|   }
 | |
| 
 | |
|   mDevice->SetPSConstantBuffer(0, &mPSBuffer0);
 | |
|   if (MaskOperation* mask = GetMask()) {
 | |
|     mDevice->SetPSTexture(kMaskLayerTextureSlot, mask->GetTexture());
 | |
|     mDevice->SetSamplerMode(kMaskSamplerSlot, SamplerMode::LinearClampToZero);
 | |
|   }
 | |
| 
 | |
|   SetupPipeline();
 | |
| 
 | |
|   if (mGeometry == GeometryMode::Polygon) {
 | |
|     mDevice->SetTopology(MLGPrimitiveTopology::UnitTriangle);
 | |
|   } else {
 | |
|     mDevice->SetTopology(MLGPrimitiveTopology::UnitQuad);
 | |
|   }
 | |
|   mDevice->SetVertexBuffer(1, &mInstanceBuffer);
 | |
| 
 | |
|   if (mGeometry == GeometryMode::Polygon) {
 | |
|     mDevice->DrawInstanced(3, mInstanceBuffer.NumVertices(), 0, 0);
 | |
|   } else {
 | |
|     mDevice->DrawInstanced(4, mInstanceBuffer.NumVertices(), 0, 0);
 | |
|   }
 | |
| }
 | |
| 
 | |
| static inline Color
 | |
| ComputeLayerColor(LayerMLGPU* aLayer, const Color& aColor)
 | |
| {
 | |
|   float opacity = aLayer->GetComputedOpacity();
 | |
|   return Color(
 | |
|     aColor.r * aColor.a * opacity,
 | |
|     aColor.g * aColor.a * opacity,
 | |
|     aColor.b * aColor.a * opacity,
 | |
|     aColor.a * opacity);
 | |
| }
 | |
| 
 | |
| ClearViewPass::ClearViewPass(FrameBuilder* aBuilder, const ItemInfo& aItem)
 | |
|  : RenderPassMLGPU(aBuilder, aItem),
 | |
|    mView(aItem.view)
 | |
| {
 | |
|   // Note: we could write to the depth buffer, but since the depth buffer is
 | |
|   // disabled by default, we don't bother yet.
 | |
|   MOZ_ASSERT(!mView->HasDepthBuffer());
 | |
| 
 | |
|   ColorLayer* colorLayer = aItem.layer->GetLayer()->AsColorLayer();
 | |
|   mColor = ComputeLayerColor(aItem.layer, colorLayer->GetColor());
 | |
| }
 | |
| 
 | |
| bool
 | |
| ClearViewPass::IsCompatible(const ItemInfo& aItem)
 | |
| {
 | |
|   if (!RenderPassMLGPU::IsCompatible(aItem)) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   // These should be true if we computed a ClearView pass type.
 | |
|   MOZ_ASSERT(aItem.translation);
 | |
|   MOZ_ASSERT(aItem.opaque);
 | |
|   MOZ_ASSERT(aItem.HasRectTransformAndClip());
 | |
| 
 | |
|   // Each call only supports a single color.
 | |
|   ColorLayer* colorLayer = aItem.layer->GetLayer()->AsColorLayer();
 | |
|   if (mColor != ComputeLayerColor(aItem.layer, colorLayer->GetColor())) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   // We don't support opacity here since it would not blend correctly.
 | |
|   MOZ_ASSERT(mColor.a == 1.0f);
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool
 | |
| ClearViewPass::AddToPass(LayerMLGPU* aItem, ItemInfo& aInfo)
 | |
| {
 | |
|   const LayerIntRegion& region = aItem->GetRenderRegion();
 | |
|   for (auto iter = region.RectIter(); !iter.Done(); iter.Next()) {
 | |
|     IntRect rect = iter.Get().ToUnknownRect();
 | |
|     rect += aInfo.translation.value();
 | |
|     rect -= mView->GetTargetOffset();
 | |
|     mRects.AppendElement(rect);
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| void
 | |
| ClearViewPass::ExecuteRendering()
 | |
| {
 | |
|   mDevice->ClearView(mDevice->GetRenderTarget(), mColor, mRects.Elements(), mRects.Length());
 | |
| }
 | |
| 
 | |
| SolidColorPass::SolidColorPass(FrameBuilder* aBuilder, const ItemInfo& aItem)
 | |
|  : BatchRenderPass(aBuilder, aItem)
 | |
| {
 | |
|   SetDefaultGeometry(aItem);
 | |
| }
 | |
| 
 | |
| bool
 | |
| SolidColorPass::AddToPass(LayerMLGPU* aLayer, ItemInfo& aInfo)
 | |
| {
 | |
|   MOZ_ASSERT(aLayer->GetType() == Layer::TYPE_COLOR);
 | |
| 
 | |
|   ColorLayer* colorLayer = aLayer->GetLayer()->AsColorLayer();
 | |
| 
 | |
|   Txn txn(this);
 | |
| 
 | |
|   gfx::Color color = ComputeLayerColor(aLayer, colorLayer->GetColor());
 | |
| 
 | |
|   const LayerIntRegion& region = aLayer->GetRenderRegion();
 | |
|   for (auto iter = region.RectIter(); !iter.Done(); iter.Next()) {
 | |
|     const IntRect rect = iter.Get().ToUnknownRect();
 | |
|     ColorTraits traits(aInfo, Rect(rect), color);
 | |
| 
 | |
|     if (!txn.Add(traits)) {
 | |
|       return false;
 | |
|     }
 | |
|   }
 | |
|   return txn.Commit();
 | |
| }
 | |
| 
 | |
| float
 | |
| SolidColorPass::GetOpacity() const
 | |
| {
 | |
|   // Note our pixel shader just ignores the opacity, since we baked it
 | |
|   // into our color values already. Just return 1, which ensures we can
 | |
|   // use the default constant buffer binding.
 | |
|   return 1.0f;
 | |
| }
 | |
| 
 | |
| void
 | |
| SolidColorPass::SetupPipeline()
 | |
| {
 | |
|   if (mGeometry == GeometryMode::UnitQuad) {
 | |
|     mDevice->SetVertexShader(VertexShaderID::ColoredQuad);
 | |
|     mDevice->SetPixelShader(PixelShaderID::ColoredQuad);
 | |
|   } else {
 | |
|     mDevice->SetVertexShader(VertexShaderID::ColoredVertex);
 | |
|     mDevice->SetPixelShader(PixelShaderID::ColoredVertex);
 | |
|   }
 | |
| }
 | |
| 
 | |
| TexturedRenderPass::TexturedRenderPass(FrameBuilder* aBuilder, const ItemInfo& aItem)
 | |
|  : BatchRenderPass(aBuilder, aItem),
 | |
|    mTextureFlags(TextureFlags::NO_FLAGS)
 | |
| {
 | |
| }
 | |
| 
 | |
| TexturedRenderPass::Info::Info(const ItemInfo& aItem, PaintedLayerMLGPU* aLayer)
 | |
|  : item(aItem),
 | |
|    textureSize(aLayer->GetTexture()->GetSize()),
 | |
|    destOrigin(aLayer->GetDestOrigin()),
 | |
|    decomposeIntoNoRepeatRects(aLayer->MayResample())
 | |
| {
 | |
| }
 | |
| 
 | |
| TexturedRenderPass::Info::Info(const ItemInfo& aItem, TexturedLayerMLGPU* aLayer)
 | |
|  : item(aItem),
 | |
|    textureSize(aLayer->GetTexture()->GetSize()),
 | |
|    scale(aLayer->GetPictureScale()),
 | |
|    decomposeIntoNoRepeatRects(false)
 | |
| {
 | |
| }
 | |
| 
 | |
| TexturedRenderPass::Info::Info(const ItemInfo& aItem, ContainerLayerMLGPU* aLayer)
 | |
|  : item(aItem),
 | |
|    textureSize(aLayer->GetTargetSize()),
 | |
|    destOrigin(aLayer->GetTargetOffset()),
 | |
|    decomposeIntoNoRepeatRects(false)
 | |
| {
 | |
| }
 | |
| 
 | |
| bool
 | |
| TexturedRenderPass::AddItem(Txn& aTxn,
 | |
|                             const Info& aInfo,
 | |
|                             const Rect& aDrawRect)
 | |
| {
 | |
|   if (mGeometry == GeometryMode::Polygon) {
 | |
|     // This path will not clamp the draw rect to the layer clip, so we can pass
 | |
|     // the draw rect texture rects straight through.
 | |
|     return AddClippedItem(aTxn, aInfo, aDrawRect);
 | |
|   }
 | |
| 
 | |
|   const ItemInfo& item = aInfo.item;
 | |
| 
 | |
|   MOZ_ASSERT(!item.geometry);
 | |
|   MOZ_ASSERT(item.HasRectTransformAndClip());
 | |
|   MOZ_ASSERT(mHasRectTransformAndClip);
 | |
| 
 | |
|   const Matrix4x4& fullTransform = item.layer->GetLayer()->GetEffectiveTransformForBuffer();
 | |
|   Matrix transform = fullTransform.As2D();
 | |
|   Matrix inverse = transform;
 | |
|   if (!inverse.Invert()) {
 | |
|     // Degenerate transforms are not visible, since there is no mapping to
 | |
|     // screen space. Just return without adding any draws.
 | |
|     return true;
 | |
|   }
 | |
|   MOZ_ASSERT(inverse.IsRectilinear());
 | |
| 
 | |
|   // Transform the clip rect.
 | |
|   IntRect clipRect = item.layer->GetComputedClipRect().ToUnknownRect();
 | |
|   clipRect += item.view->GetTargetOffset();
 | |
| 
 | |
|   // Clip and adjust the texture rect.
 | |
|   Rect localClip = inverse.TransformBounds(Rect(clipRect));
 | |
|   Rect clippedDrawRect = aDrawRect.Intersect(localClip);
 | |
|   if (clippedDrawRect.IsEmpty()) {
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   return AddClippedItem(aTxn, aInfo, clippedDrawRect);
 | |
| }
 | |
| 
 | |
| bool
 | |
| TexturedRenderPass::AddClippedItem(Txn& aTxn,
 | |
|                                    const Info& aInfo,
 | |
|                                    const gfx::Rect& aDrawRect)
 | |
| {
 | |
|   float xScale = 1.0;
 | |
|   float yScale = 1.0;
 | |
|   if (aInfo.scale) {
 | |
|     xScale = aInfo.scale->width;
 | |
|     yScale = aInfo.scale->height;
 | |
|   }
 | |
| 
 | |
|   Point offset = aDrawRect.TopLeft() - aInfo.destOrigin;
 | |
|   Rect textureRect(
 | |
|     offset.x * xScale,
 | |
|     offset.y * yScale,
 | |
|     aDrawRect.Width() * xScale,
 | |
|     aDrawRect.Height() * yScale);
 | |
| 
 | |
|   Rect textureCoords = TextureRectToCoords(textureRect, aInfo.textureSize);
 | |
|   if (mTextureFlags & TextureFlags::ORIGIN_BOTTOM_LEFT) {
 | |
|     textureCoords.MoveToY(1.0 - textureCoords.Y());
 | |
|     textureCoords.SetHeight(-textureCoords.Height());
 | |
|   }
 | |
| 
 | |
|   if (!aInfo.decomposeIntoNoRepeatRects) {
 | |
|     // Fast, normal case, we can use the texture coordinates as-s and the caller
 | |
|     // will use a repeat sampler if needed.
 | |
|     TexturedTraits traits(aInfo.item, aDrawRect, textureCoords);
 | |
|     if (!aTxn.Add(traits)) {
 | |
|       return false;
 | |
|     }
 | |
|   } else {
 | |
|     Rect layerRects[4];
 | |
|     Rect textureRects[4];
 | |
|     size_t numRects =
 | |
|       DecomposeIntoNoRepeatRects(aDrawRect, textureCoords, &layerRects, &textureRects);
 | |
| 
 | |
|     for (size_t i = 0; i < numRects; i++) {
 | |
|       TexturedTraits traits(aInfo.item, layerRects[i], textureRects[i]);
 | |
|       if (!aTxn.Add(traits)) {
 | |
|         return false;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| SingleTexturePass::SingleTexturePass(FrameBuilder* aBuilder, const ItemInfo& aItem)
 | |
|  : TexturedRenderPass(aBuilder, aItem),
 | |
|    mSamplerMode(SamplerMode::LinearClamp),
 | |
|    mOpacity(1.0f)
 | |
| {
 | |
|   SetDefaultGeometry(aItem);
 | |
| }
 | |
| 
 | |
| bool
 | |
| SingleTexturePass::AddToPass(LayerMLGPU* aLayer, ItemInfo& aItem)
 | |
| {
 | |
|   RefPtr<TextureSource> texture;
 | |
| 
 | |
|   SamplerMode sampler;
 | |
|   TextureFlags flags = TextureFlags::NO_FLAGS;
 | |
|   if (PaintedLayerMLGPU* paintedLayer = aLayer->AsPaintedLayerMLGPU()) {
 | |
|     if (paintedLayer->HasComponentAlpha()) {
 | |
|       return false;
 | |
|     }
 | |
|     texture = paintedLayer->GetTexture();
 | |
|     sampler = paintedLayer->GetSamplerMode();
 | |
|   } else if (TexturedLayerMLGPU* texLayer = aLayer->AsTexturedLayerMLGPU()) {
 | |
|     texture = texLayer->GetTexture();
 | |
|     sampler = FilterToSamplerMode(texLayer->GetSamplingFilter());
 | |
|     TextureHost* host = texLayer->GetImageHost()->CurrentTextureHost();
 | |
|     flags = host->GetFlags();
 | |
|   } else {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   // We should not assign a texture-based layer to tiles if it has no texture.
 | |
|   MOZ_ASSERT(texture);
 | |
| 
 | |
|   float opacity = aLayer->GetComputedOpacity();
 | |
|   if (mTexture) {
 | |
|     if (texture != mTexture) {
 | |
|       return false;
 | |
|     }
 | |
|     if (mSamplerMode != sampler) {
 | |
|       return false;
 | |
|     }
 | |
|     if (mOpacity != opacity) {
 | |
|       return false;
 | |
|     }
 | |
|     // Note: premultiplied, origin-bottom-left are already implied by the texture source.
 | |
|   } else {
 | |
|     mTexture = texture;
 | |
|     mSamplerMode = sampler;
 | |
|     mOpacity = opacity;
 | |
|     mTextureFlags = flags;
 | |
|   }
 | |
| 
 | |
|   Txn txn(this);
 | |
| 
 | |
|   // Note: these are two separate cases since no Info constructor takes in a
 | |
|   // base LayerMLGPU class.
 | |
|   if (PaintedLayerMLGPU* layer = aLayer->AsPaintedLayerMLGPU()) {
 | |
|     Info info(aItem, layer);
 | |
|     if (!AddItems(txn, info, layer->GetDrawRects())) {
 | |
|       return false;
 | |
|     }
 | |
|   } else if (TexturedLayerMLGPU* layer = aLayer->AsTexturedLayerMLGPU()) {
 | |
|     Info info(aItem, layer);
 | |
|     if (!AddItems(txn, info, layer->GetRenderRegion())) {
 | |
|       return false;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return txn.Commit();
 | |
| }
 | |
| 
 | |
| Maybe<MLGBlendState>
 | |
| SingleTexturePass::GetBlendState() const
 | |
| {
 | |
|   return (mTextureFlags & TextureFlags::NON_PREMULTIPLIED)
 | |
|          ? Some(MLGBlendState::OverAndPremultiply)
 | |
|          : Some(MLGBlendState::Over);
 | |
| }
 | |
| 
 | |
| void
 | |
| SingleTexturePass::SetupPipeline()
 | |
| {
 | |
|   MOZ_ASSERT(mTexture);
 | |
| 
 | |
|   if (mGeometry == GeometryMode::UnitQuad) {
 | |
|     mDevice->SetVertexShader(VertexShaderID::TexturedQuad);
 | |
|   } else {
 | |
|     mDevice->SetVertexShader(VertexShaderID::TexturedVertex);
 | |
|   }
 | |
| 
 | |
|   mDevice->SetPSTexture(0, mTexture);
 | |
|   mDevice->SetSamplerMode(kDefaultSamplerSlot, mSamplerMode);
 | |
|   switch (mTexture.get()->GetFormat()) {
 | |
|     case SurfaceFormat::B8G8R8A8:
 | |
|     case SurfaceFormat::R8G8B8A8:
 | |
|       if (mGeometry == GeometryMode::UnitQuad)
 | |
|         mDevice->SetPixelShader(PixelShaderID::TexturedQuadRGBA);
 | |
|       else
 | |
|         mDevice->SetPixelShader(PixelShaderID::TexturedVertexRGBA);
 | |
|       break;
 | |
|     default:
 | |
|       if (mGeometry == GeometryMode::UnitQuad)
 | |
|         mDevice->SetPixelShader(PixelShaderID::TexturedQuadRGB);
 | |
|       else
 | |
|         mDevice->SetPixelShader(PixelShaderID::TexturedVertexRGB);
 | |
|       break;
 | |
|   }
 | |
| }
 | |
| 
 | |
| ComponentAlphaPass::ComponentAlphaPass(FrameBuilder* aBuilder, const ItemInfo& aItem)
 | |
|  : TexturedRenderPass(aBuilder, aItem),
 | |
|    mOpacity(1.0f),
 | |
|    mSamplerMode(SamplerMode::LinearClamp)
 | |
| {
 | |
|   SetDefaultGeometry(aItem);
 | |
| }
 | |
| 
 | |
| bool
 | |
| ComponentAlphaPass::AddToPass(LayerMLGPU* aLayer, ItemInfo& aItem)
 | |
| {
 | |
|   PaintedLayerMLGPU* layer = aLayer->AsPaintedLayerMLGPU();
 | |
|   MOZ_ASSERT(layer);
 | |
| 
 | |
|   if (mTextureOnBlack) {
 | |
|     if (layer->GetTexture() != mTextureOnBlack ||
 | |
|         layer->GetTextureOnWhite() != mTextureOnWhite ||
 | |
|         layer->GetOpacity() != mOpacity ||
 | |
|         layer->GetSamplerMode() != mSamplerMode)
 | |
|     {
 | |
|       return false;
 | |
|     }
 | |
|   } else {
 | |
|     mOpacity = layer->GetComputedOpacity();
 | |
|     mSamplerMode = layer->GetSamplerMode();
 | |
|     mTextureOnBlack = layer->GetTexture();
 | |
|     mTextureOnWhite = layer->GetTextureOnWhite();
 | |
|   }
 | |
| 
 | |
|   Txn txn(this);
 | |
| 
 | |
|   Info info(aItem, layer);
 | |
|   if (!AddItems(txn, info, layer->GetDrawRects())) {
 | |
|     return false;
 | |
|   }
 | |
|   return txn.Commit();
 | |
| }
 | |
| 
 | |
| float
 | |
| ComponentAlphaPass::GetOpacity() const
 | |
| {
 | |
|   return mOpacity;
 | |
| }
 | |
| 
 | |
| void
 | |
| ComponentAlphaPass::SetupPipeline()
 | |
| {
 | |
|   TextureSource* textures[2] = {
 | |
|     mTextureOnBlack,
 | |
|     mTextureOnWhite
 | |
|   };
 | |
|   MOZ_ASSERT(textures[0]);
 | |
|   MOZ_ASSERT(textures[1]);
 | |
| 
 | |
|   if (mGeometry == GeometryMode::UnitQuad) {
 | |
|     mDevice->SetVertexShader(VertexShaderID::TexturedQuad);
 | |
|     mDevice->SetPixelShader(PixelShaderID::ComponentAlphaQuad);
 | |
|   } else {
 | |
|     mDevice->SetVertexShader(VertexShaderID::TexturedVertex);
 | |
|     mDevice->SetPixelShader(PixelShaderID::ComponentAlphaVertex);
 | |
|   }
 | |
| 
 | |
|   mDevice->SetSamplerMode(kDefaultSamplerSlot, mSamplerMode);
 | |
|   mDevice->SetPSTextures(0, 2, textures);
 | |
| }
 | |
| 
 | |
| VideoRenderPass::VideoRenderPass(FrameBuilder* aBuilder, const ItemInfo& aItem)
 | |
|  : TexturedRenderPass(aBuilder, aItem),
 | |
|    mSamplerMode(SamplerMode::LinearClamp),
 | |
|    mOpacity(1.0f)
 | |
| {
 | |
|   SetDefaultGeometry(aItem);
 | |
| }
 | |
| 
 | |
| bool
 | |
| VideoRenderPass::AddToPass(LayerMLGPU* aLayer, ItemInfo& aItem)
 | |
| {
 | |
|   ImageLayerMLGPU* layer = aLayer->AsImageLayerMLGPU();
 | |
|   if (!layer) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   RefPtr<TextureHost> host = layer->GetImageHost()->CurrentTextureHost();
 | |
|   RefPtr<TextureSource> source = layer->GetTexture();
 | |
|   float opacity = layer->GetComputedOpacity();
 | |
|   SamplerMode sampler = FilterToSamplerMode(layer->GetSamplingFilter());
 | |
| 
 | |
|   if (mHost) {
 | |
|     if (mHost != host) {
 | |
|       return false;
 | |
|     }
 | |
|     if (mTexture != source) {
 | |
|       return false;
 | |
|     }
 | |
|     if (mOpacity != opacity) {
 | |
|       return false;
 | |
|     }
 | |
|     if (mSamplerMode != sampler) {
 | |
|       return false;
 | |
|     }
 | |
|   } else {
 | |
|     mHost = host;
 | |
|     mTexture = source;
 | |
|     mOpacity = opacity;
 | |
|     mSamplerMode = sampler;
 | |
|   }
 | |
|   MOZ_ASSERT(!mTexture->AsBigImageIterator());
 | |
|   MOZ_ASSERT(!(mHost->GetFlags() & TextureFlags::NON_PREMULTIPLIED));
 | |
|   MOZ_ASSERT(!(mHost->GetFlags() & TextureFlags::ORIGIN_BOTTOM_LEFT));
 | |
| 
 | |
|   Txn txn(this);
 | |
| 
 | |
|   Info info(aItem, layer);
 | |
|   if (!AddItems(txn, info, layer->GetRenderRegion())) {
 | |
|     return false;
 | |
|   }
 | |
|   return txn.Commit();
 | |
| }
 | |
| 
 | |
| void
 | |
| VideoRenderPass::SetupPipeline()
 | |
| {
 | |
|   YUVColorSpace colorSpace = YUVColorSpace::UNKNOWN;
 | |
|   switch (mHost->GetReadFormat()) {
 | |
|   case SurfaceFormat::YUV: {
 | |
|     colorSpace = mHost->GetYUVColorSpace();
 | |
|     break;
 | |
|   }
 | |
|   case SurfaceFormat::NV12:
 | |
|     colorSpace = YUVColorSpace::BT601;
 | |
|     break;
 | |
|   default:
 | |
|     MOZ_ASSERT_UNREACHABLE("Unexpected surface format in VideoRenderPass");
 | |
|     break;
 | |
|   }
 | |
|   MOZ_ASSERT(colorSpace != YUVColorSpace::UNKNOWN);
 | |
| 
 | |
|   RefPtr<MLGBuffer> ps1 = mDevice->GetBufferForColorSpace(colorSpace);
 | |
|   if (!ps1) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (mGeometry == GeometryMode::UnitQuad) {
 | |
|     mDevice->SetVertexShader(VertexShaderID::TexturedQuad);
 | |
|   } else {
 | |
|     mDevice->SetVertexShader(VertexShaderID::TexturedVertex);
 | |
|   }
 | |
| 
 | |
|   switch (mHost->GetReadFormat()) {
 | |
|   case SurfaceFormat::YUV:
 | |
|   {
 | |
|     if (mGeometry == GeometryMode::UnitQuad)
 | |
|       mDevice->SetPixelShader(PixelShaderID::TexturedQuadIMC4);
 | |
|     else
 | |
|       mDevice->SetPixelShader(PixelShaderID::TexturedVertexIMC4);
 | |
|     mDevice->SetPSTexturesYUV(0, mTexture);
 | |
|     break;
 | |
|   }
 | |
|   case SurfaceFormat::NV12:
 | |
|     if (mGeometry == GeometryMode::UnitQuad)
 | |
|       mDevice->SetPixelShader(PixelShaderID::TexturedQuadNV12);
 | |
|     else
 | |
|       mDevice->SetPixelShader(PixelShaderID::TexturedVertexNV12);
 | |
|     mDevice->SetPSTexturesNV12(0, mTexture);
 | |
|     break;
 | |
|   default:
 | |
|     MOZ_ASSERT_UNREACHABLE("Unknown video format");
 | |
|     break;
 | |
|   }
 | |
| 
 | |
|   mDevice->SetSamplerMode(kDefaultSamplerSlot, mSamplerMode);
 | |
|   mDevice->SetPSConstantBuffer(1, ps1);
 | |
| }
 | |
| 
 | |
| RenderViewPass::RenderViewPass(FrameBuilder* aBuilder, const ItemInfo& aItem)
 | |
|  : TexturedRenderPass(aBuilder, aItem),
 | |
|    mParentView(nullptr)
 | |
| {
 | |
|   mAssignedLayer = aItem.layer->AsContainerLayerMLGPU();
 | |
| 
 | |
|   CompositionOp blendOp = mAssignedLayer->GetMixBlendMode();
 | |
|   if (BlendOpIsMixBlendMode(blendOp)) {
 | |
|     mBlendMode = Some(blendOp);
 | |
|   }
 | |
| 
 | |
|   if (mBlendMode) {
 | |
|     // We do not have fast-path rect shaders for blending.
 | |
|     SetGeometry(aItem, GeometryMode::Polygon);
 | |
|   } else {
 | |
|     SetDefaultGeometry(aItem);
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool
 | |
| RenderViewPass::AddToPass(LayerMLGPU* aLayer, ItemInfo& aItem)
 | |
| {
 | |
|   // We bake in the layer ahead of time, which also guarantees the blend mode
 | |
|   // is baked in, as well as the geometry requirement.
 | |
|   if (mAssignedLayer != aLayer) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   mSource = mAssignedLayer->GetRenderTarget();
 | |
|   if (!mSource) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   mParentView = aItem.view;
 | |
| 
 | |
|   Txn txn(this);
 | |
| 
 | |
|   IntPoint offset = mAssignedLayer->GetTargetOffset();
 | |
|   IntSize size = mAssignedLayer->GetTargetSize();
 | |
| 
 | |
|   // Clamp the visible region to the texture size.
 | |
|   nsIntRegion visible = mAssignedLayer->GetRenderRegion().ToUnknownRegion();
 | |
|   visible.AndWith(IntRect(offset, size));
 | |
| 
 | |
|   Info info(aItem, mAssignedLayer);
 | |
|   if (!AddItems(txn, info, visible)) {
 | |
|     return false;
 | |
|   }
 | |
|   return txn.Commit();
 | |
| }
 | |
| 
 | |
| float
 | |
| RenderViewPass::GetOpacity() const
 | |
| {
 | |
|   return mAssignedLayer->GetLayer()->GetEffectiveOpacity();
 | |
| }
 | |
| 
 | |
| bool
 | |
| RenderViewPass::OnPrepareBuffers()
 | |
| {
 | |
|   if (mBlendMode && !PrepareBlendState()) {
 | |
|     return false;
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| static inline PixelShaderID
 | |
| GetShaderForBlendMode(CompositionOp aOp)
 | |
| {
 | |
|   switch (aOp) {
 | |
|   case CompositionOp::OP_MULTIPLY: return PixelShaderID::BlendMultiply;
 | |
|   case CompositionOp::OP_SCREEN: return PixelShaderID::BlendScreen;
 | |
|   case CompositionOp::OP_OVERLAY: return PixelShaderID::BlendOverlay;
 | |
|   case CompositionOp::OP_DARKEN: return PixelShaderID::BlendDarken;
 | |
|   case CompositionOp::OP_LIGHTEN: return PixelShaderID::BlendLighten;
 | |
|   case CompositionOp::OP_COLOR_DODGE: return PixelShaderID::BlendColorDodge;
 | |
|   case CompositionOp::OP_COLOR_BURN: return PixelShaderID::BlendColorBurn;
 | |
|   case CompositionOp::OP_HARD_LIGHT: return PixelShaderID::BlendHardLight;
 | |
|   case CompositionOp::OP_SOFT_LIGHT: return PixelShaderID::BlendSoftLight;
 | |
|   case CompositionOp::OP_DIFFERENCE: return PixelShaderID::BlendDifference;
 | |
|   case CompositionOp::OP_EXCLUSION: return PixelShaderID::BlendExclusion;
 | |
|   case CompositionOp::OP_HUE: return PixelShaderID::BlendHue;
 | |
|   case CompositionOp::OP_SATURATION: return PixelShaderID::BlendSaturation;
 | |
|   case CompositionOp::OP_COLOR: return PixelShaderID::BlendColor;
 | |
|   case CompositionOp::OP_LUMINOSITY: return PixelShaderID::BlendLuminosity;
 | |
|   default:
 | |
|     MOZ_ASSERT_UNREACHABLE("Unexpected blend mode");
 | |
|     return PixelShaderID::TexturedVertexRGBA;
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool
 | |
| RenderViewPass::PrepareBlendState()
 | |
| {
 | |
|   Rect visibleRect(mAssignedLayer->GetRenderRegion().GetBounds().ToUnknownRect());
 | |
|   IntRect clipRect(mAssignedLayer->GetComputedClipRect().ToUnknownRect());
 | |
|   const Matrix4x4& transform = mAssignedLayer->GetLayer()->GetEffectiveTransformForBuffer();
 | |
| 
 | |
|   // Note that we must use our parent RenderView for this calculation,
 | |
|   // since we're copying the backdrop, not our actual local target.
 | |
|   IntRect rtRect(mParentView->GetTargetOffset(), mParentView->GetSize());
 | |
| 
 | |
|   Matrix4x4 backdropTransform;
 | |
|   mBackdropCopyRect = ComputeBackdropCopyRect(
 | |
|     visibleRect,
 | |
|     clipRect,
 | |
|     transform,
 | |
|     rtRect,
 | |
|     &backdropTransform);
 | |
| 
 | |
|   AutoBufferUpload<BlendVertexShaderConstants> cb;
 | |
|   if (!mDevice->GetSharedVSBuffer()->Allocate(&mBlendConstants, &cb)) {
 | |
|     return false;
 | |
|   }
 | |
|   memcpy(cb->backdropTransform, &backdropTransform._11, 64);
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| void
 | |
| RenderViewPass::SetupPipeline()
 | |
| {
 | |
|   if (mBlendMode) {
 | |
|     RefPtr<MLGRenderTarget> backdrop = mParentView->GetRenderTarget();
 | |
|     MOZ_ASSERT(mDevice->GetRenderTarget() == backdrop);
 | |
| 
 | |
|     RefPtr<MLGTexture> copy = mDevice->CreateTexture(
 | |
|       mBackdropCopyRect.Size(),
 | |
|       SurfaceFormat::B8G8R8A8,
 | |
|       MLGUsage::Default,
 | |
|       MLGTextureFlags::ShaderResource);
 | |
|     if (!copy) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     mDevice->CopyTexture(
 | |
|       copy,
 | |
|       IntPoint(0, 0),
 | |
|       backdrop->GetTexture(),
 | |
|       mBackdropCopyRect);
 | |
| 
 | |
|     MOZ_ASSERT(mGeometry == GeometryMode::Polygon);
 | |
|     mDevice->SetVertexShader(VertexShaderID::BlendVertex);
 | |
|     mDevice->SetPixelShader(GetShaderForBlendMode(mBlendMode.value()));
 | |
|     mDevice->SetVSConstantBuffer(kBlendConstantBufferSlot, &mBlendConstants);
 | |
|     mDevice->SetPSTexture(1, copy);
 | |
|   } else {
 | |
|     if (mGeometry == GeometryMode::UnitQuad) {
 | |
|       mDevice->SetVertexShader(VertexShaderID::TexturedQuad);
 | |
|       mDevice->SetPixelShader(PixelShaderID::TexturedQuadRGBA);
 | |
|     } else {
 | |
|       mDevice->SetVertexShader(VertexShaderID::TexturedVertex);
 | |
|       mDevice->SetPixelShader(PixelShaderID::TexturedVertexRGBA);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   mDevice->SetPSTexture(0, mSource->GetTexture());
 | |
|   mDevice->SetSamplerMode(kDefaultSamplerSlot, SamplerMode::LinearClamp);
 | |
| }
 | |
| 
 | |
| void
 | |
| RenderViewPass::ExecuteRendering()
 | |
| {
 | |
|   if (mAssignedLayer->NeedsSurfaceCopy()) {
 | |
|     RenderWithBackdropCopy();
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   TexturedRenderPass::ExecuteRendering();
 | |
| }
 | |
| 
 | |
| void
 | |
| RenderViewPass::RenderWithBackdropCopy()
 | |
| {
 | |
|   MOZ_ASSERT(mAssignedLayer->NeedsSurfaceCopy());
 | |
| 
 | |
|   DebugOnly<Matrix> transform2d;
 | |
|   const Matrix4x4& transform = mAssignedLayer->GetEffectiveTransform();
 | |
|   MOZ_ASSERT(transform.Is2D(&transform2d) &&
 | |
|              !gfx::ThebesMatrix(transform2d).HasNonIntegerTranslation());
 | |
| 
 | |
|   IntPoint translation = IntPoint::Truncate(transform._41, transform._42);
 | |
| 
 | |
|   RenderViewMLGPU* childView = mAssignedLayer->GetRenderView();
 | |
| 
 | |
|   IntRect visible = mAssignedLayer->GetRenderRegion().GetBounds().ToUnknownRect();
 | |
|   IntRect sourceRect = visible + translation - mParentView->GetTargetOffset();
 | |
|   IntPoint destPoint = visible.TopLeft() - childView->GetTargetOffset();
 | |
| 
 | |
|   RefPtr<MLGTexture> dest = mAssignedLayer->GetRenderTarget()->GetTexture();
 | |
|   RefPtr<MLGTexture> source = mParentView->GetRenderTarget()->GetTexture();
 | |
| 
 | |
|   // Clamp the source rect to the source texture size.
 | |
|   sourceRect = sourceRect.Intersect(IntRect(IntPoint(0, 0), source->GetSize()));
 | |
| 
 | |
|   // Clamp the source rect to the destination texture size.
 | |
|   IntRect destRect(destPoint, sourceRect.Size());
 | |
|   destRect = destRect.Intersect(IntRect(IntPoint(0, 0), dest->GetSize()));
 | |
|   sourceRect = sourceRect.Intersect(IntRect(sourceRect.TopLeft(), destRect.Size()));
 | |
| 
 | |
|   mDevice->CopyTexture(dest, destPoint, source, sourceRect);
 | |
|   childView->RenderAfterBackdropCopy();
 | |
|   mParentView->RestoreDeviceState();
 | |
|   TexturedRenderPass::ExecuteRendering();
 | |
| }
 | |
| 
 | |
| } // namespace layers
 | |
| } // namespace mozilla
 |