зеркало из https://github.com/mozilla/gecko-dev.git
1057 строки
30 KiB
C++
1057 строки
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(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),
|
|
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)
|
|
{
|
|
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),
|
|
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
|