diff options
Diffstat (limited to '')
-rw-r--r-- | gfx/layers/mlgpu/RenderPassMLGPU.cpp | 971 |
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 |