summaryrefslogtreecommitdiffstats
path: root/gfx/layers/mlgpu/RenderPassMLGPU.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--gfx/layers/mlgpu/RenderPassMLGPU.cpp971
1 files changed, 971 insertions, 0 deletions
diff --git a/gfx/layers/mlgpu/RenderPassMLGPU.cpp b/gfx/layers/mlgpu/RenderPassMLGPU.cpp
new file mode 100644
index 0000000000..1fd970e4a8
--- /dev/null
+++ b/gfx/layers/mlgpu/RenderPassMLGPU.cpp
@@ -0,0 +1,971 @@
+/* -*- 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 "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 ||
+ texture->GetReadFormat() == SurfaceFormat::P010 ||
+ texture->GetReadFormat() == SurfaceFormat::P016) {
+ 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() = default;
+
+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 DeviceColor ComputeLayerColor(LayerMLGPU* aLayer,
+ const DeviceColor& aColor) {
+ float opacity = aLayer->GetComputedOpacity();
+ return DeviceColor(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::DeviceColor 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:
+ case SurfaceFormat::NV12:
+ case SurfaceFormat::P010:
+ case SurfaceFormat::P016:
+ colorSpace = mHost->GetYUVColorSpace();
+ 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;
+ }
+
+ RefPtr<MLGBuffer> ps2 =
+ mDevice->GetBufferForColorDepthCoefficient(mHost->GetColorDepth());
+ if (!ps2) {
+ return;
+ }
+
+ if (mGeometry == GeometryMode::UnitQuad) {
+ mDevice->SetVertexShader(VertexShaderID::TexturedQuad);
+ } else {
+ mDevice->SetVertexShader(VertexShaderID::TexturedVertex);
+ }
+
+ switch (mHost->GetReadFormat()) {
+ case SurfaceFormat::YUV: {
+ if (colorSpace == YUVColorSpace::Identity) {
+ if (mGeometry == GeometryMode::UnitQuad)
+ mDevice->SetPixelShader(PixelShaderID::TexturedQuadIdentityIMC4);
+ else
+ mDevice->SetPixelShader(PixelShaderID::TexturedVertexIdentityIMC4);
+ } else {
+ if (mGeometry == GeometryMode::UnitQuad)
+ mDevice->SetPixelShader(PixelShaderID::TexturedQuadIMC4);
+ else
+ mDevice->SetPixelShader(PixelShaderID::TexturedVertexIMC4);
+ }
+ mDevice->SetPSTexturesYUV(0, mTexture);
+ break;
+ }
+ case SurfaceFormat::NV12:
+ case SurfaceFormat::P010:
+ case SurfaceFormat::P016:
+ 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);
+ mDevice->SetPSConstantBuffer(2, ps2);
+}
+
+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