Bug 1632310 [wpt PR 23186] - Add test for computed versus resolved style., a=testonly
[gecko.git] / gfx / layers / mlgpu / MaskOperation.cpp
blob9976ec4f62e3bfdd071fb1d9f6b50b126a021d80
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "MaskOperation.h"
8 #include "FrameBuilder.h"
9 #include "LayerMLGPU.h"
10 #include "mozilla/layers/LayersHelpers.h"
11 #include "MLGDevice.h"
12 #include "TexturedLayerMLGPU.h"
14 namespace mozilla {
15 namespace layers {
17 using namespace gfx;
19 MaskOperation::MaskOperation(FrameBuilder* aBuilder) {}
21 MaskOperation::MaskOperation(FrameBuilder* aBuilder, MLGTexture* aSource)
22 : mTexture(aSource) {}
24 MaskOperation::~MaskOperation() = default;
26 static gfx::Rect ComputeQuadForMaskLayer(Layer* aLayer, const IntSize& aSize) {
27 const Matrix4x4& transform = aLayer->GetEffectiveTransform();
28 MOZ_ASSERT(transform.Is2D(), "Mask layers should not have 3d transforms");
30 Rect bounds(Point(0, 0), Size(aSize));
31 return transform.As2D().TransformBounds(bounds);
34 Rect MaskOperation::ComputeMaskRect(Layer* aLayer) const {
35 Layer* maskLayer = aLayer->GetMaskLayer() ? aLayer->GetMaskLayer()
36 : aLayer->GetAncestorMaskLayerAt(0);
37 MOZ_ASSERT(
38 (aLayer->GetAncestorMaskLayerCount() == 0 && aLayer->GetMaskLayer()) ||
39 (aLayer->GetAncestorMaskLayerCount() == 1 && !aLayer->GetMaskLayer()));
41 return ComputeQuadForMaskLayer(maskLayer, mTexture->GetSize());
44 // This is only needed for std::map.
45 bool MaskTexture::operator<(const MaskTexture& aOther) const {
46 if (mRect.X() != aOther.mRect.X()) {
47 return mRect.X() < aOther.mRect.X();
49 if (mRect.Y() != aOther.mRect.Y()) {
50 return mRect.Y() < aOther.mRect.Y();
52 if (mRect.Width() != aOther.mRect.Width()) {
53 return mRect.Width() < aOther.mRect.Width();
55 if (mRect.Height() != aOther.mRect.Height()) {
56 return mRect.Height() < aOther.mRect.Height();
58 return mSource < aOther.mSource;
61 RefPtr<TextureSource> GetMaskLayerTexture(Layer* aLayer) {
62 LayerMLGPU* layer = aLayer->AsHostLayer()->AsLayerMLGPU();
63 TexturedLayerMLGPU* texLayer = layer->AsTexturedLayerMLGPU();
64 if (!texLayer) {
65 MOZ_ASSERT_UNREACHABLE("Mask layers should be texture layers");
66 return nullptr;
69 RefPtr<TextureSource> source = texLayer->BindAndGetTexture();
70 if (!source) {
71 gfxWarning() << "Mask layer does not have a TextureSource";
72 return nullptr;
74 return source;
77 MaskCombineOperation::MaskCombineOperation(FrameBuilder* aBuilder)
78 : MaskOperation(aBuilder), mBuilder(aBuilder) {}
80 MaskCombineOperation::~MaskCombineOperation() = default;
82 void MaskCombineOperation::Init(const MaskTextureList& aTextures) {
83 // All masks for a single layer exist in the same coordinate space. Find the
84 // area that covers all rects.
85 Rect area = aTextures[0].mRect;
86 for (size_t i = 1; i < aTextures.size(); i++) {
87 area = area.Intersect(aTextures[i].mRect);
90 // Go through and decide which areas of the textures are relevant.
91 for (size_t i = 0; i < aTextures.size(); i++) {
92 Rect rect = aTextures[i].mRect.Intersect(area);
93 if (rect.IsEmpty()) {
94 continue;
97 rect -= aTextures[i].mRect.TopLeft();
98 mTextures.push_back(MaskTexture(rect, aTextures[i].mSource));
101 IntRect size;
102 Rect bounds = area;
103 bounds.RoundOut();
104 bounds.ToIntRect(&size);
106 if (size.IsEmpty()) {
107 return;
110 mTarget = mBuilder->GetDevice()->CreateRenderTarget(size.Size());
111 if (mTarget) {
112 mTexture = mTarget->GetTexture();
114 mArea = area;
117 void MaskCombineOperation::PrepareForRendering() {
118 for (const auto& entry : mTextures) {
119 Rect texCoords = TextureRectToCoords(entry.mRect, entry.mSource->GetSize());
121 SharedVertexBuffer* shared = mBuilder->GetDevice()->GetSharedVertexBuffer();
123 VertexBufferSection section;
124 if (!shared->Allocate(&section, 1, sizeof(texCoords), &texCoords)) {
125 continue;
127 mInputBuffers.push_back(section);
131 void MaskCombineOperation::Render() {
132 if (!mTarget) {
133 return;
136 RefPtr<MLGDevice> device = mBuilder->GetDevice();
138 device->SetTopology(MLGPrimitiveTopology::UnitQuad);
139 device->SetVertexShader(VertexShaderID::MaskCombiner);
141 device->SetPixelShader(PixelShaderID::MaskCombiner);
142 device->SetSamplerMode(0, SamplerMode::LinearClamp);
143 device->SetBlendState(MLGBlendState::Min);
145 // Since the mask operation is effectively an AND operation, we initialize
146 // the entire r-channel to 1.
147 device->Clear(mTarget, DeviceColor(1, 0, 0, 1));
148 device->SetScissorRect(Nothing());
149 device->SetRenderTarget(mTarget);
150 device->SetViewport(IntRect(IntPoint(0, 0), mTarget->GetSize()));
152 for (size_t i = 0; i < mInputBuffers.size(); i++) {
153 if (!mInputBuffers[i].IsValid()) {
154 continue;
156 device->SetVertexBuffer(1, &mInputBuffers[i]);
157 device->SetPSTexture(0, mTextures[i].mSource);
158 device->DrawInstanced(4, mInputBuffers[i].NumVertices(), 0, 0);
162 void AppendToMaskTextureList(MaskTextureList& aList, Layer* aLayer) {
163 RefPtr<TextureSource> source = GetMaskLayerTexture(aLayer);
164 if (!source) {
165 return;
168 gfx::Rect rect = ComputeQuadForMaskLayer(aLayer, source->GetSize());
169 aList.push_back(MaskTexture(rect, source));
172 } // namespace layers
173 } // namespace mozilla