fune/gfx/layers/mlgpu/RenderPassMLGPU.cpp
Jean-Yves Avenard 4aac6e0243 Bug 1659015 - P2. Add YUV identity support (rgb) to Advanced Layers. r=bas
It's destined to go, but for now it's required.

Depends on D87167

Differential Revision: https://phabricator.services.mozilla.com/D87168
2020-08-17 16:04:50 +00:00

972 lines
32 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 ||
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