Line data Source code
1 : /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : * This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "FrameBuilder.h"
7 : #include "ContainerLayerMLGPU.h"
8 : #include "GeckoProfiler.h" // for profiler_*
9 : #include "LayerMLGPU.h"
10 : #include "LayerManagerMLGPU.h"
11 : #include "MaskOperation.h"
12 : #include "RenderPassMLGPU.h"
13 : #include "RenderViewMLGPU.h"
14 : #include "mozilla/gfx/Polygon.h"
15 : #include "mozilla/layers/BSPTree.h"
16 : #include "mozilla/layers/LayersHelpers.h"
17 :
18 : namespace mozilla {
19 : namespace layers {
20 :
21 : using namespace mlg;
22 :
23 0 : FrameBuilder::FrameBuilder(LayerManagerMLGPU* aManager, MLGSwapChain* aSwapChain)
24 : : mManager(aManager),
25 : mDevice(aManager->GetDevice()),
26 0 : mSwapChain(aSwapChain)
27 : {
28 : // test_bug1124898.html has a root ColorLayer, so we don't assume the root is
29 : // a container.
30 0 : mRoot = mManager->GetRoot()->AsHostLayer()->AsLayerMLGPU();
31 0 : }
32 :
33 0 : FrameBuilder::~FrameBuilder()
34 : {
35 0 : }
36 :
37 : bool
38 0 : FrameBuilder::Build()
39 : {
40 0 : AUTO_PROFILER_LABEL("FrameBuilder::Build", GRAPHICS);
41 :
42 : // AcquireBackBuffer can fail, so we check the result here.
43 0 : RefPtr<MLGRenderTarget> target = mSwapChain->AcquireBackBuffer();
44 0 : if (!target) {
45 0 : return false;
46 : }
47 :
48 : // This updates the frame sequence number, so layers can quickly check if
49 : // they've already been prepared.
50 0 : LayerMLGPU::BeginFrame();
51 :
52 : // Note: we don't clip draw calls to the invalid region per se, but instead
53 : // the region bounds. Clipping all draw calls would incur a significant
54 : // CPU cost on large layer trees, and would greatly complicate how draw
55 : // rects are added in RenderPassMLGPU, since we would need to break
56 : // each call into additional items based on the intersection with the
57 : // invalid region.
58 : //
59 : // Instead we scissor to the invalid region bounds. As a result, all items
60 : // affecting the invalid bounds are redrawn, even if not all are in the
61 : // precise region.
62 0 : const nsIntRegion& region = mSwapChain->GetBackBufferInvalidRegion();
63 :
64 0 : mWidgetRenderView = new RenderViewMLGPU(this, target, region);
65 :
66 : // Traverse the layer tree and assign each layer to tiles.
67 : {
68 0 : Maybe<gfx::Polygon> geometry;
69 0 : RenderTargetIntRect clip(0, 0, target->GetSize().width, target->GetSize().height);
70 :
71 0 : AssignLayer(mRoot->GetLayer(), mWidgetRenderView, clip, Move(geometry));
72 : }
73 :
74 : // Build the default mask buffer.
75 : {
76 0 : MaskInformation defaultMaskInfo(1.0f, false);
77 0 : if (!mDevice->GetSharedPSBuffer()->Allocate(&mDefaultMaskInfo, defaultMaskInfo)) {
78 0 : return false;
79 : }
80 : }
81 :
82 : // Build render passes and buffer information for each pass.
83 0 : mWidgetRenderView->FinishBuilding();
84 0 : mWidgetRenderView->Prepare();
85 :
86 : // Prepare masks that need to be combined.
87 0 : for (const auto& pair : mCombinedTextureMasks) {
88 0 : pair.second->PrepareForRendering();
89 : }
90 :
91 0 : FinishCurrentLayerBuffer();
92 0 : FinishCurrentMaskRectBuffer();
93 0 : return true;
94 : }
95 :
96 : void
97 0 : FrameBuilder::Render()
98 : {
99 0 : AUTO_PROFILER_LABEL("FrameBuilder::Render", GRAPHICS);
100 :
101 : // Render combined masks into single mask textures.
102 0 : for (const auto& pair : mCombinedTextureMasks) {
103 0 : pair.second->Render();
104 : }
105 :
106 : // Render to all targets, front-to-back.
107 0 : mWidgetRenderView->Render();
108 0 : }
109 :
110 : void
111 0 : FrameBuilder::AssignLayer(Layer* aLayer,
112 : RenderViewMLGPU* aView,
113 : const RenderTargetIntRect& aClipRect,
114 : Maybe<gfx::Polygon>&& aGeometry)
115 : {
116 0 : LayerMLGPU* layer = aLayer->AsHostLayer()->AsLayerMLGPU();
117 :
118 0 : if (ContainerLayer* container = aLayer->AsContainerLayer()) {
119 : // This returns false if we don't need to (or can't) process the layer any
120 : // further. This always returns false for non-leaf ContainerLayers.
121 0 : if (!ProcessContainerLayer(container, aView, aClipRect, aGeometry)) {
122 0 : return;
123 : }
124 : } else {
125 : // Set the precomputed clip and any textures/resources that are needed.
126 0 : if (!layer->PrepareToRender(this, aClipRect)) {
127 0 : return;
128 : }
129 : }
130 :
131 : // If we are dealing with a nested 3D context, we might need to transform
132 : // the geometry back to the coordinate space of the current layer.
133 0 : if (aGeometry) {
134 0 : TransformLayerGeometry(aLayer, aGeometry);
135 : }
136 :
137 : // Finally, assign the layer to a rendering batch in the current render
138 : // target.
139 0 : layer->AssignToView(this, aView, Move(aGeometry));
140 : }
141 :
142 : bool
143 0 : FrameBuilder::ProcessContainerLayer(ContainerLayer* aContainer,
144 : RenderViewMLGPU* aView,
145 : const RenderTargetIntRect& aClipRect,
146 : Maybe<gfx::Polygon>& aGeometry)
147 : {
148 0 : LayerMLGPU* layer = aContainer->AsHostLayer()->AsLayerMLGPU();
149 :
150 : // We don't want to traverse containers twice, so we only traverse them if
151 : // they haven't been prepared yet.
152 0 : bool isFirstVisit = !layer->IsPrepared();
153 0 : if (isFirstVisit && !layer->PrepareToRender(this, aClipRect)) {
154 0 : return false;
155 : }
156 :
157 : // If the container is not part of the invalid region, we don't draw it
158 : // or traverse it. Note that we do not pass the geometry here. Otherwise
159 : // we could decide the particular split is not visible, and because of the
160 : // check above, never bother traversing the container again.
161 0 : gfx::IntRect boundingBox = layer->GetClippedBoundingBox(aView, Nothing());
162 0 : const gfx::IntRect& invalidRect = aView->GetInvalidRect();
163 0 : if (boundingBox.IsEmpty() || !invalidRect.Intersects(boundingBox)) {
164 0 : return false;
165 : }
166 :
167 0 : if (!aContainer->UseIntermediateSurface()) {
168 : // In case the layer previously required an intermediate surface, we
169 : // clear any intermediate render targets here.
170 0 : layer->ClearCachedResources();
171 :
172 : // This is a pass-through container, so we just process children and
173 : // instruct AssignLayer to early-return.
174 0 : ProcessChildList(aContainer, aView, aClipRect, aGeometry);
175 0 : return false;
176 : }
177 :
178 : // If this is the first visit of the container this frame, and the
179 : // container has an unpainted area, we traverse the container. Note that
180 : // RefLayers do not have intermediate surfaces so this is guaranteed
181 : // to be a full-fledged ContainerLayerMLGPU.
182 0 : ContainerLayerMLGPU* viewContainer = layer->AsContainerLayerMLGPU();
183 0 : if (isFirstVisit && !viewContainer->GetInvalidRect().IsEmpty()) {
184 : // The RenderView constructor automatically attaches itself to the parent.
185 0 : RefPtr<RenderViewMLGPU> view = new RenderViewMLGPU(this, viewContainer, aView);
186 0 : ProcessChildList(aContainer, view, aClipRect, Nothing());
187 0 : view->FinishBuilding();
188 : }
189 0 : return true;
190 : }
191 :
192 : void
193 0 : FrameBuilder::ProcessChildList(ContainerLayer* aContainer,
194 : RenderViewMLGPU* aView,
195 : const RenderTargetIntRect& aParentClipRect,
196 : const Maybe<gfx::Polygon>& aParentGeometry)
197 : {
198 : nsTArray<LayerPolygon> polygons =
199 0 : aContainer->SortChildrenBy3DZOrder(ContainerLayer::SortMode::WITH_GEOMETRY);
200 :
201 : // Visit layers in front-to-back order.
202 0 : for (auto iter = polygons.rbegin(); iter != polygons.rend(); iter++) {
203 0 : LayerPolygon& entry = *iter;
204 0 : Layer* child = entry.layer;
205 0 : if (child->IsBackfaceHidden() || !child->IsVisible()) {
206 0 : continue;
207 : }
208 :
209 0 : RenderTargetIntRect clip = child->CalculateScissorRect(aParentClipRect);
210 0 : if (clip.IsEmpty()) {
211 0 : continue;
212 : }
213 :
214 0 : Maybe<gfx::Polygon> geometry;
215 0 : if (aParentGeometry && entry.geometry) {
216 : // Both parent and child are split.
217 0 : geometry = Some(aParentGeometry->ClipPolygon(*entry.geometry));
218 0 : } else if (aParentGeometry) {
219 0 : geometry = aParentGeometry;
220 0 : } else if (entry.geometry) {
221 0 : geometry = Move(entry.geometry);
222 : }
223 :
224 0 : AssignLayer(child, aView, clip, Move(geometry));
225 : }
226 0 : }
227 :
228 : bool
229 0 : FrameBuilder::AddLayerToConstantBuffer(ItemInfo& aItem)
230 : {
231 0 : LayerMLGPU* layer = aItem.layer;
232 :
233 : // If this layer could appear multiple times, cache it.
234 0 : if (aItem.geometry) {
235 0 : if (mLayerBufferMap.Get(layer, &aItem.layerIndex)) {
236 0 : return true;
237 : }
238 : }
239 :
240 0 : LayerConstants* info = AllocateLayerInfo(aItem);
241 0 : if (!info) {
242 0 : return false;
243 : }
244 :
245 : // Note we do not use GetEffectiveTransformForBuffer, since we calculate
246 : // the correct scaling when we build texture coordinates.
247 0 : Layer* baseLayer = layer->GetLayer();
248 0 : const gfx::Matrix4x4& transform = baseLayer->GetEffectiveTransform();
249 :
250 0 : memcpy(&info->transform, &transform._11, 64);
251 0 : info->clipRect = gfx::Rect(layer->GetComputedClipRect().ToUnknownRect());
252 0 : info->maskIndex = 0;
253 0 : if (MaskOperation* op = layer->GetMask()) {
254 : // Note: we use 0 as an invalid index, and so indices are offset by 1.
255 0 : gfx::Rect rect = op->ComputeMaskRect(baseLayer);
256 0 : AddMaskRect(rect, &info->maskIndex);
257 : }
258 :
259 0 : if (aItem.geometry) {
260 0 : mLayerBufferMap.Put(layer, aItem.layerIndex);
261 : }
262 0 : return true;
263 : }
264 :
265 : MaskOperation*
266 0 : FrameBuilder::AddMaskOperation(LayerMLGPU* aLayer)
267 : {
268 0 : Layer* layer = aLayer->GetLayer();
269 0 : MOZ_ASSERT(layer->HasMaskLayers());
270 :
271 : // Multiple masks are combined into a single mask.
272 0 : if ((layer->GetMaskLayer() && layer->GetAncestorMaskLayerCount()) ||
273 0 : layer->GetAncestorMaskLayerCount() > 1)
274 : {
275 : // Since each mask can be moved independently of the other, we must create
276 : // a separate combined mask for every new positioning we encounter.
277 0 : MaskTextureList textures;
278 0 : if (Layer* maskLayer = layer->GetMaskLayer()) {
279 0 : AppendToMaskTextureList(textures, maskLayer);
280 : }
281 0 : for (size_t i = 0; i < layer->GetAncestorMaskLayerCount(); i++) {
282 0 : AppendToMaskTextureList(textures, layer->GetAncestorMaskLayerAt(i));
283 : }
284 :
285 0 : auto iter = mCombinedTextureMasks.find(textures);
286 0 : if (iter != mCombinedTextureMasks.end()) {
287 0 : return iter->second;
288 : }
289 :
290 0 : RefPtr<MaskCombineOperation> op = new MaskCombineOperation(this);
291 0 : op->Init(textures);
292 :
293 0 : mCombinedTextureMasks[textures] = op;
294 0 : return op;
295 : }
296 :
297 0 : Layer* maskLayer = layer->GetMaskLayer()
298 0 : ? layer->GetMaskLayer()
299 0 : : layer->GetAncestorMaskLayerAt(0);
300 0 : RefPtr<TextureSource> texture = GetMaskLayerTexture(maskLayer);
301 0 : if (!texture) {
302 0 : return nullptr;
303 : }
304 :
305 0 : RefPtr<MaskOperation> op;
306 0 : mSingleTextureMasks.Get(texture, getter_AddRefs(op));
307 0 : if (op) {
308 0 : return op;
309 : }
310 :
311 0 : RefPtr<MLGTexture> wrapped = mDevice->CreateTexture(texture);
312 :
313 0 : op = new MaskOperation(this, wrapped);
314 0 : mSingleTextureMasks.Put(texture, op);
315 0 : return op;
316 : }
317 :
318 : void
319 0 : FrameBuilder::RetainTemporaryLayer(LayerMLGPU* aLayer)
320 : {
321 : // This should only be used with temporary layers. Temporary layers do not
322 : // have parents.
323 0 : MOZ_ASSERT(!aLayer->GetLayer()->GetParent());
324 0 : mTemporaryLayers.push_back(aLayer->GetLayer());
325 0 : }
326 :
327 : LayerConstants*
328 0 : FrameBuilder::AllocateLayerInfo(ItemInfo& aItem)
329 : {
330 0 : if (((mCurrentLayerBuffer.Length() + 1) * sizeof(LayerConstants)) >
331 0 : mDevice->GetMaxConstantBufferBindSize())
332 : {
333 0 : FinishCurrentLayerBuffer();
334 0 : mLayerBufferMap.Clear();
335 0 : mCurrentLayerBuffer.ClearAndRetainStorage();
336 : }
337 :
338 0 : LayerConstants* info = mCurrentLayerBuffer.AppendElement(mozilla::fallible);
339 0 : if (!info) {
340 0 : return nullptr;
341 : }
342 :
343 0 : aItem.layerIndex = mCurrentLayerBuffer.Length() - 1;
344 0 : return info;
345 : }
346 :
347 : void
348 0 : FrameBuilder::FinishCurrentLayerBuffer()
349 : {
350 0 : if (mCurrentLayerBuffer.IsEmpty()) {
351 0 : return;
352 : }
353 :
354 : // Note: we append the buffer even if we couldn't allocate one, since
355 : // that keeps the indices sane.
356 0 : ConstantBufferSection section;
357 0 : mDevice->GetSharedVSBuffer()->Allocate(
358 : §ion,
359 0 : mCurrentLayerBuffer.Elements(),
360 0 : mCurrentLayerBuffer.Length());
361 0 : mLayerBuffers.AppendElement(section);
362 : }
363 :
364 : size_t
365 0 : FrameBuilder::CurrentLayerBufferIndex() const
366 : {
367 : // The mask rect buffer list doesn't contain the buffer currently being
368 : // built, so we don't subtract 1 here.
369 0 : return mLayerBuffers.Length();
370 : }
371 :
372 : ConstantBufferSection
373 0 : FrameBuilder::GetLayerBufferByIndex(size_t aIndex) const
374 : {
375 0 : if (aIndex >= mLayerBuffers.Length()) {
376 0 : return ConstantBufferSection();
377 : }
378 0 : return mLayerBuffers[aIndex];
379 : }
380 :
381 : bool
382 0 : FrameBuilder::AddMaskRect(const gfx::Rect& aRect, uint32_t* aOutIndex)
383 : {
384 0 : if (((mCurrentMaskRectList.Length() + 1) * sizeof(gfx::Rect)) >
385 0 : mDevice->GetMaxConstantBufferBindSize())
386 : {
387 0 : FinishCurrentMaskRectBuffer();
388 0 : mCurrentMaskRectList.ClearAndRetainStorage();
389 : }
390 :
391 0 : mCurrentMaskRectList.AppendElement(aRect);
392 :
393 : // Mask indices start at 1 so the shader can use 0 as a no-mask indicator.
394 0 : *aOutIndex = mCurrentMaskRectList.Length();
395 0 : return true;
396 : }
397 :
398 : void
399 0 : FrameBuilder::FinishCurrentMaskRectBuffer()
400 : {
401 0 : if (mCurrentMaskRectList.IsEmpty()) {
402 0 : return;
403 : }
404 :
405 : // Note: we append the buffer even if we couldn't allocate one, since
406 : // that keeps the indices sane.
407 0 : ConstantBufferSection section;
408 0 : mDevice->GetSharedVSBuffer()->Allocate(
409 : §ion,
410 0 : mCurrentMaskRectList.Elements(),
411 0 : mCurrentMaskRectList.Length());
412 0 : mMaskRectBuffers.AppendElement(section);
413 : }
414 :
415 : size_t
416 0 : FrameBuilder::CurrentMaskRectBufferIndex() const
417 : {
418 : // The mask rect buffer list doesn't contain the buffer currently being
419 : // built, so we don't subtract 1 here.
420 0 : return mMaskRectBuffers.Length();
421 : }
422 :
423 : ConstantBufferSection
424 0 : FrameBuilder::GetMaskRectBufferByIndex(size_t aIndex) const
425 : {
426 0 : if (aIndex >= mMaskRectBuffers.Length()) {
427 0 : return ConstantBufferSection();
428 : }
429 0 : return mMaskRectBuffers[aIndex];
430 : }
431 :
432 : } // namespace layers
433 9 : } // namespace mozilla
|