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 "RenderViewMLGPU.h"
7 : #include "ContainerLayerMLGPU.h"
8 : #include "FrameBuilder.h"
9 : #include "gfxPrefs.h"
10 : #include "LayersHelpers.h"
11 : #include "LayersLogging.h"
12 : #include "MLGDevice.h"
13 : #include "RenderPassMLGPU.h"
14 : #include "ShaderDefinitionsMLGPU.h"
15 : #include "UtilityMLGPU.h"
16 :
17 : namespace mozilla {
18 : namespace layers {
19 :
20 : using namespace gfx;
21 :
22 0 : RenderViewMLGPU::RenderViewMLGPU(FrameBuilder* aBuilder,
23 : MLGRenderTarget* aTarget,
24 0 : const nsIntRegion& aInvalidRegion)
25 0 : : RenderViewMLGPU(aBuilder, nullptr)
26 : {
27 0 : mTarget = aTarget;
28 0 : mInvalidBounds = aInvalidRegion.GetBounds();
29 :
30 : // The clear region on the layer manager is the area that must be clear after
31 : // we finish drawing.
32 0 : mPostClearRegion = aBuilder->GetManager()->GetRegionToClear();
33 :
34 : // Clamp the post-clear region to the invalid bounds, since clears don't go
35 : // through the scissor rect if using ClearView.
36 0 : mPostClearRegion.AndWith(mInvalidBounds);
37 :
38 : // Since the post-clear will occlude everything, we include it in the final
39 : // opaque area.
40 : mOccludedRegion.OrWith(
41 0 : ViewAs<LayerPixel>(mPostClearRegion, PixelCastJustification::RenderTargetIsParentLayerForRoot));
42 :
43 : AL_LOG("RenderView %p root with invalid area %s, clear area %s\n",
44 : this,
45 : Stringify(mInvalidBounds).c_str(),
46 : Stringify(mPostClearRegion).c_str());
47 0 : }
48 :
49 0 : RenderViewMLGPU::RenderViewMLGPU(FrameBuilder* aBuilder,
50 : ContainerLayerMLGPU* aContainer,
51 0 : RenderViewMLGPU* aParent)
52 0 : : RenderViewMLGPU(aBuilder, aParent)
53 : {
54 0 : mContainer = aContainer;
55 0 : mTargetOffset = aContainer->GetTargetOffset();
56 0 : mInvalidBounds = aContainer->GetInvalidRect();
57 0 : MOZ_ASSERT(!mInvalidBounds.IsEmpty());
58 :
59 : AL_LOG("RenderView %p starting with container %p and invalid area %s\n",
60 : this,
61 : aContainer->GetLayer(),
62 : Stringify(mInvalidBounds).c_str());
63 0 : }
64 :
65 0 : RenderViewMLGPU::RenderViewMLGPU(FrameBuilder* aBuilder, RenderViewMLGPU* aParent)
66 : : mBuilder(aBuilder),
67 : mDevice(aBuilder->GetDevice()),
68 : mParent(aParent),
69 : mContainer(nullptr),
70 : mFinishedBuilding(false),
71 : mCurrentLayerBufferIndex(kInvalidResourceIndex),
72 : mCurrentMaskRectBufferIndex(kInvalidResourceIndex),
73 : mNextSortIndex(1),
74 0 : mUseDepthBuffer(gfxPrefs::AdvancedLayersEnableDepthBuffer()),
75 0 : mDepthBufferNeedsClear(false)
76 : {
77 0 : if (aParent) {
78 0 : aParent->AddChild(this);
79 : }
80 0 : }
81 :
82 0 : RenderViewMLGPU::~RenderViewMLGPU()
83 : {
84 0 : for (const auto& child : mChildren) {
85 0 : child->mParent = nullptr;
86 : }
87 0 : }
88 :
89 : IntSize
90 0 : RenderViewMLGPU::GetSize() const
91 : {
92 0 : MOZ_ASSERT(mFinishedBuilding);
93 0 : return mTarget->GetSize();
94 : }
95 :
96 : MLGRenderTarget*
97 0 : RenderViewMLGPU::GetRenderTarget() const
98 : {
99 0 : MOZ_ASSERT(mFinishedBuilding);
100 0 : return mTarget;
101 : }
102 :
103 : void
104 0 : RenderViewMLGPU::AddChild(RenderViewMLGPU* aParent)
105 : {
106 0 : mChildren.push_back(aParent);
107 0 : }
108 :
109 : void
110 0 : RenderViewMLGPU::Render()
111 : {
112 : // We render tiles front-to-back, depth-first, to minimize render target switching.
113 0 : for (const auto& child : mChildren) {
114 0 : child->Render();
115 : }
116 :
117 0 : ExecuteRendering();
118 0 : }
119 :
120 : void
121 0 : RenderViewMLGPU::FinishBuilding()
122 : {
123 0 : MOZ_ASSERT(!mFinishedBuilding);
124 0 : mFinishedBuilding = true;
125 :
126 0 : if (mContainer) {
127 0 : MOZ_ASSERT(!mTarget);
128 :
129 0 : MLGRenderTargetFlags flags = MLGRenderTargetFlags::Default;
130 0 : if (mUseDepthBuffer) {
131 0 : flags |= MLGRenderTargetFlags::ZBuffer;
132 : }
133 0 : mTarget = mContainer->UpdateRenderTarget(mDevice, flags);
134 : }
135 0 : }
136 :
137 : void
138 0 : RenderViewMLGPU::AddItem(LayerMLGPU* aItem,
139 : const IntRect& aRect,
140 : Maybe<Polygon>&& aGeometry)
141 : {
142 : AL_LOG("RenderView %p analyzing layer %p\n", this, aItem->GetLayer());
143 :
144 : // If the item is not visible at all, skip it.
145 0 : if (aItem->GetComputedOpacity() == 0.0f) {
146 : AL_LOG("RenderView %p culling item %p with no opacity\n",
147 : this,
148 : aItem->GetLayer());
149 0 : return;
150 : }
151 :
152 : // When using the depth buffer, the z-index for items is important.
153 : //
154 : // Sort order starts at 1 and goes to positive infinity, with smaller values
155 : // being closer to the screen. Our viewport is the same, with anything
156 : // outside of [0.0, 1.0] being culled, and lower values occluding higher
157 : // values. To make this work our projection transform scales the z-axis.
158 : // Note that we do not use 0 as a sorting index (when depth-testing is
159 : // enabled) because this would result in a z-value of 1.0, which would be
160 : // culled.
161 0 : ItemInfo info(mBuilder, this, aItem, mNextSortIndex++, aRect, Move(aGeometry));
162 :
163 : // If the item is not visible, or we can't add it to the layer constant
164 : // buffer for some reason, bail out.
165 0 : if (!UpdateVisibleRegion(info) || !mBuilder->AddLayerToConstantBuffer(info)) {
166 : AL_LOG("RenderView %p culled item %p!\n", this, aItem->GetLayer());
167 0 : return;
168 : }
169 :
170 : // We support all layer types now.
171 0 : MOZ_ASSERT(info.type != RenderPassType::Unknown);
172 :
173 0 : if (info.renderOrder == RenderOrder::FrontToBack) {
174 0 : AddItemFrontToBack(aItem, info);
175 : } else {
176 0 : AddItemBackToFront(aItem, info);
177 : }
178 : }
179 :
180 : bool
181 0 : RenderViewMLGPU::UpdateVisibleRegion(ItemInfo& aItem)
182 : {
183 : // If the item has some kind of complex transform, we perform a very
184 : // simple occlusion test and move on. We using a depth buffer we skip
185 : // CPU-based occlusion culling as well, since the GPU will do most of our
186 : // culling work for us.
187 0 : if (mUseDepthBuffer ||
188 0 : !aItem.translation ||
189 0 : !gfxPrefs::AdvancedLayersEnableCPUOcclusion())
190 : {
191 : // Update the render region even if we won't compute visibility, since some
192 : // layer types (like Canvas and Image) need to have the visible region
193 : // clamped.
194 0 : LayerIntRegion region = Move(aItem.layer->GetShadowVisibleRegion());
195 0 : aItem.layer->SetRegionToRender(Move(region));
196 :
197 : AL_LOG("RenderView %p simple occlusion test, bounds=%s, translation?=%d\n",
198 : this,
199 : Stringify(aItem.bounds).c_str(),
200 : aItem.translation ? 1 : 0);
201 0 : return mInvalidBounds.Intersects(aItem.bounds);
202 : }
203 :
204 0 : MOZ_ASSERT(aItem.rectilinear);
205 :
206 : AL_LOG("RenderView %p starting visibility tests:\n", this);
207 : AL_LOG(" occluded=%s\n", Stringify(mOccludedRegion).c_str());
208 :
209 : // Compute the translation into render target space.
210 : LayerIntPoint translation =
211 0 : LayerIntPoint::FromUnknownPoint(aItem.translation.value() - mTargetOffset);
212 : AL_LOG(" translation=%s\n", Stringify(translation).c_str());
213 :
214 0 : IntRect clip = aItem.layer->GetComputedClipRect().ToUnknownRect();
215 : AL_LOG(" clip=%s\n", Stringify(translation).c_str());
216 :
217 0 : LayerIntRegion region = Move(aItem.layer->GetShadowVisibleRegion());
218 0 : region.MoveBy(translation);
219 : AL_LOG(" effective-visible=%s\n", Stringify(region).c_str());
220 :
221 0 : region.SubOut(mOccludedRegion);
222 0 : region.AndWith(LayerIntRect::FromUnknownRect(mInvalidBounds));
223 0 : region.AndWith(LayerIntRect::FromUnknownRect(clip));
224 0 : if (region.IsEmpty()) {
225 0 : return false;
226 : }
227 :
228 : // Move the visible region back into layer space.
229 0 : region.MoveBy(-translation);
230 : AL_LOG(" new-local-visible=%s\n", Stringify(region).c_str());
231 :
232 0 : aItem.layer->SetRegionToRender(Move(region));
233 :
234 : // Apply the new occluded area. We do another dance with the translation to
235 : // avoid copying the region. We do this after the SetRegionToRender call to
236 : // accomodate the possiblity of a layer changing its visible region.
237 0 : if (aItem.opaque) {
238 0 : mOccludedRegion.MoveBy(-translation);
239 0 : mOccludedRegion.OrWith(aItem.layer->GetShadowVisibleRegion());
240 0 : mOccludedRegion.MoveBy(translation);
241 : AL_LOG(" new-occluded=%s\n", Stringify(mOccludedRegion).c_str());
242 :
243 : // If the occluded region gets too complicated, we reset it.
244 0 : if (mOccludedRegion.GetNumRects() >= 32) {
245 0 : mOccludedRegion.SetEmpty();
246 : AL_LOG(" clear-occluded, too many rects\n");
247 : }
248 : }
249 0 : return true;
250 : }
251 :
252 : void
253 0 : RenderViewMLGPU::AddItemFrontToBack(LayerMLGPU* aLayer, ItemInfo& aItem)
254 : {
255 : // We receive items in front-to-back order. Ideally we want to push items
256 : // as far back into batches impossible, to ensure the GPU can do a good
257 : // job at culling. However we also want to make sure we actually batch
258 : // items versus drawing one primitive per pass.
259 : //
260 : // As a compromise we look at the most 3 recent batches and then give up.
261 : // This can be tweaked in the future.
262 : static const size_t kMaxSearch = 3;
263 0 : size_t iterations = 0;
264 0 : for (auto iter = mFrontToBack.rbegin(); iter != mFrontToBack.rend(); iter++) {
265 0 : RenderPassMLGPU* pass = (*iter);
266 0 : if (pass->IsCompatible(aItem) && pass->AcceptItem(aItem)) {
267 : AL_LOG("RenderView %p added layer %p to pass %p (%d)\n",
268 : this, aLayer->GetLayer(), pass, int(pass->GetType()));
269 0 : return;
270 : }
271 0 : if (++iterations > kMaxSearch) {
272 0 : break;
273 : }
274 : }
275 :
276 0 : RefPtr<RenderPassMLGPU> pass = RenderPassMLGPU::CreatePass(mBuilder, aItem);
277 0 : if (!pass || !pass->AcceptItem(aItem)) {
278 0 : MOZ_ASSERT_UNREACHABLE("Could not build a pass for item!");
279 : return;
280 : }
281 : AL_LOG("RenderView %p added layer %p to new pass %p (%d)\n",
282 : this, aLayer->GetLayer(), pass.get(), int(pass->GetType()));
283 :
284 0 : mFrontToBack.push_back(pass);
285 : }
286 :
287 : void
288 0 : RenderViewMLGPU::AddItemBackToFront(LayerMLGPU* aLayer, ItemInfo& aItem)
289 : {
290 : // We receive layers in front-to-back order, but there are two cases when we
291 : // actually draw back-to-front: when the depth buffer is disabled, or when
292 : // using the depth buffer and the item has transparent pixels (and therefore
293 : // requires blending). In these cases we will build vertex and constant
294 : // buffers in reverse, as well as execute batches in reverse, to ensure the
295 : // correct ordering.
296 : //
297 : // Note: We limit the number of batches we search through, since it's better
298 : // to add new draw calls than spend too much time finding compatible
299 : // batches further down.
300 : static const size_t kMaxSearch = 10;
301 0 : size_t iterations = 0;
302 0 : for (auto iter = mBackToFront.begin(); iter != mBackToFront.end(); iter++) {
303 0 : RenderPassMLGPU* pass = (*iter);
304 0 : if (pass->IsCompatible(aItem) && pass->AcceptItem(aItem)) {
305 : AL_LOG("RenderView %p added layer %p to pass %p (%d)\n",
306 : this, aLayer->GetLayer(), pass, int(pass->GetType()));
307 0 : return;
308 : }
309 0 : if (pass->Intersects(aItem)) {
310 0 : break;
311 : }
312 0 : if (++iterations > kMaxSearch) {
313 0 : break;
314 : }
315 : }
316 :
317 0 : RefPtr<RenderPassMLGPU> pass = RenderPassMLGPU::CreatePass(mBuilder, aItem);
318 0 : if (!pass || !pass->AcceptItem(aItem)) {
319 0 : MOZ_ASSERT_UNREACHABLE("Could not build a pass for item!");
320 : return;
321 : }
322 : AL_LOG("RenderView %p added layer %p to new pass %p (%d)\n",
323 : this, aLayer->GetLayer(), pass.get(), int(pass->GetType()));
324 :
325 0 : mBackToFront.push_front(pass);
326 : }
327 :
328 : void
329 0 : RenderViewMLGPU::Prepare()
330 : {
331 0 : if (!mTarget) {
332 0 : return;
333 : }
334 :
335 : // Prepare front-to-back passes. These are only present when using the depth
336 : // buffer, and they contain only opaque data.
337 0 : for (RefPtr<RenderPassMLGPU>& pass : mFrontToBack) {
338 0 : pass->PrepareForRendering();
339 : }
340 :
341 : // Prepare the Clear buffer, which will fill the render target with transparent
342 : // pixels. This must happen before we set up world constants, since it can
343 : // create new z-indices.
344 0 : PrepareClears();
345 :
346 : // Prepare the world constant buffer. This must be called after we've
347 : // finished allocating all z-indices.
348 : {
349 0 : WorldConstants vsConstants;
350 0 : Matrix4x4 projection = Matrix4x4::Translation(-1.0, 1.0, 0.0);
351 0 : projection.PreScale(2.0 / float(mTarget->GetSize().width),
352 0 : 2.0 / float(mTarget->GetSize().height),
353 0 : 1.0f);
354 0 : projection.PreScale(1.0f, -1.0f, 1.0f);
355 :
356 0 : memcpy(vsConstants.projection, &projection._11, 64);
357 0 : vsConstants.targetOffset = Point(mTargetOffset);
358 0 : vsConstants.sortIndexOffset = PrepareDepthBuffer();
359 :
360 0 : SharedConstantBuffer* shared = mDevice->GetSharedVSBuffer();
361 0 : if (!shared->Allocate(&mWorldConstants, vsConstants)) {
362 0 : return;
363 : }
364 : }
365 :
366 : // Prepare back-to-front passes. In depth buffer mode, these contain draw
367 : // calls that might produce transparent pixels. When using CPU-based occlusion
368 : // culling, all draw calls are back-to-front.
369 0 : for (RefPtr<RenderPassMLGPU>& pass : mBackToFront) {
370 0 : pass->PrepareForRendering();
371 : }
372 :
373 : // Now, process children.
374 0 : for (const auto& iter : mChildren) {
375 0 : iter->Prepare();
376 : }
377 : }
378 :
379 : void
380 0 : RenderViewMLGPU::ExecuteRendering()
381 : {
382 0 : if (!mTarget) {
383 0 : return;
384 : }
385 :
386 0 : mDevice->SetRenderTarget(mTarget);
387 0 : mDevice->SetViewport(IntRect(IntPoint(0, 0), mTarget->GetSize()));
388 0 : mDevice->SetScissorRect(Some(mInvalidBounds));
389 :
390 0 : if (!mWorldConstants.IsValid()) {
391 0 : gfxWarning() << "Failed to allocate constant buffer for world transform";
392 0 : return;
393 : }
394 0 : mDevice->SetVSConstantBuffer(kWorldConstantBufferSlot, &mWorldConstants);
395 :
396 : // If using the depth buffer, clear it (if needed) and enable writes.
397 0 : if (mUseDepthBuffer) {
398 0 : if (mDepthBufferNeedsClear) {
399 0 : mDevice->ClearDepthBuffer(mTarget);
400 : }
401 0 : mDevice->SetDepthTestMode(MLGDepthTestMode::Write);
402 : }
403 :
404 : // Opaque items, rendered front-to-back.
405 0 : for (auto iter = mFrontToBack.begin(); iter != mFrontToBack.end(); iter++) {
406 0 : ExecutePass(*iter);
407 : }
408 :
409 0 : if (mUseDepthBuffer) {
410 : // From now on we might be rendering transparent pixels, so we disable
411 : // writing to the z-buffer.
412 0 : mDevice->SetDepthTestMode(MLGDepthTestMode::ReadOnly);
413 : }
414 :
415 : // Clear any pixels that are not occluded, and therefore might require
416 : // blending.
417 0 : mDevice->DrawClearRegion(mPreClear);
418 :
419 : // Render back-to-front passes.
420 0 : for (auto iter = mBackToFront.begin(); iter != mBackToFront.end(); iter++) {
421 0 : ExecutePass(*iter);
422 : }
423 :
424 : // Make sure the post-clear area has no pixels.
425 0 : if (!mPostClearRegion.IsEmpty()) {
426 0 : mDevice->DrawClearRegion(mPostClear);
427 : }
428 :
429 : // We repaint the entire invalid region, even if it is partially occluded.
430 : // Thus it's safe for us to clear the invalid area here. If we ever switch
431 : // to nsIntRegions, we will have to take the difference between the paitned
432 : // area and the invalid area.
433 0 : if (mContainer) {
434 0 : mContainer->ClearInvalidRect();
435 : }
436 : }
437 :
438 : void
439 0 : RenderViewMLGPU::ExecutePass(RenderPassMLGPU* aPass)
440 : {
441 0 : if (!aPass->IsPrepared()) {
442 0 : return;
443 : }
444 :
445 : // Change the layer buffer if needed.
446 0 : if (aPass->GetLayerBufferIndex() != mCurrentLayerBufferIndex) {
447 0 : mCurrentLayerBufferIndex = aPass->GetLayerBufferIndex();
448 :
449 0 : ConstantBufferSection section = mBuilder->GetLayerBufferByIndex(mCurrentLayerBufferIndex);
450 0 : mDevice->SetVSConstantBuffer(kLayerBufferSlot, §ion);
451 : }
452 :
453 : // Change the mask rect buffer if needed.
454 0 : if (aPass->GetMaskRectBufferIndex() &&
455 0 : aPass->GetMaskRectBufferIndex().value() != mCurrentMaskRectBufferIndex)
456 : {
457 0 : mCurrentMaskRectBufferIndex = aPass->GetMaskRectBufferIndex().value();
458 :
459 0 : ConstantBufferSection section = mBuilder->GetMaskRectBufferByIndex(mCurrentMaskRectBufferIndex);
460 0 : mDevice->SetVSConstantBuffer(kMaskBufferSlot, §ion);
461 : }
462 :
463 : // Change the blend state if needed.
464 0 : if (Maybe<MLGBlendState> blendState = aPass->GetBlendState()) {
465 0 : mDevice->SetBlendState(blendState.value());
466 : }
467 :
468 0 : aPass->ExecuteRendering();
469 : }
470 :
471 : int32_t
472 0 : RenderViewMLGPU::PrepareDepthBuffer()
473 : {
474 0 : if (!mUseDepthBuffer) {
475 0 : return 0;
476 : }
477 :
478 : // Rather than clear the depth buffer every frame, we offset z-indices each
479 : // frame, starting with indices far away from the screen and moving toward
480 : // the user each successive frame. This ensures that frames can re-use the
481 : // depth buffer but never collide with previously written values.
482 : //
483 : // Once a frame runs out of sort indices, we finally clear the depth buffer
484 : // and start over again.
485 :
486 : // Note: the lowest sort index (kDepthLimit) is always occluded since it will
487 : // resolve to the clear value - kDepthLimit / kDepthLimit == 1.0.
488 : //
489 : // If we don't have any more indices to allocate, we need to clear the depth
490 : // buffer and start fresh.
491 0 : int32_t highestIndex = mTarget->GetLastDepthStart();
492 0 : if (highestIndex < mNextSortIndex) {
493 0 : mDepthBufferNeedsClear = true;
494 0 : highestIndex = kDepthLimit;
495 : }
496 :
497 : // We should not have more than kDepthLimit layers to draw. The last known
498 : // sort index might appear in the depth buffer and occlude something, so
499 : // we subtract 1. This ensures all our indices will compare less than all
500 : // old indices.
501 0 : int32_t sortOffset = highestIndex - mNextSortIndex - 1;
502 0 : MOZ_ASSERT(sortOffset >= 0);
503 :
504 0 : mTarget->SetLastDepthStart(sortOffset);
505 0 : return sortOffset;
506 : }
507 :
508 : void
509 0 : RenderViewMLGPU::PrepareClears()
510 : {
511 : // Get the list of rects to clear. If using the depth buffer, we don't
512 : // care if it's accurate since the GPU will do occlusion testing for us.
513 : // If not using the depth buffer, we subtract out the occluded region.
514 0 : LayerIntRegion region = LayerIntRect::FromUnknownRect(mInvalidBounds);
515 0 : if (!mUseDepthBuffer) {
516 : // Don't let the clear region become too complicated.
517 0 : region.SubOut(mOccludedRegion);
518 0 : region.SimplifyOutward(kMaxClearViewRects);
519 : }
520 :
521 0 : Maybe<int32_t> sortIndex;
522 0 : if (mUseDepthBuffer) {
523 : // Note that we use the lowest available sorting index, to ensure that when
524 : // using the z-buffer, we don't draw over already-drawn content.
525 0 : sortIndex = Some(mNextSortIndex++);
526 : }
527 :
528 0 : nsTArray<IntRect> rects = ToRectArray(region);
529 0 : mDevice->PrepareClearRegion(&mPreClear, Move(rects), sortIndex);
530 :
531 0 : if (!mPostClearRegion.IsEmpty()) {
532 : // Prepare the final clear as well. Note that we always do this clear at the
533 : // very end, even when the depth buffer is enabled, so we don't bother
534 : // setting a useful sorting index. If and when we try to ship the depth
535 : // buffer, we would execute this clear earlier in the pipeline and give it
536 : // the closest possible z-ordering to the screen.
537 0 : nsTArray<IntRect> rects = ToRectArray(mPostClearRegion);
538 0 : mDevice->PrepareClearRegion(&mPostClear, Move(rects), Nothing());
539 : }
540 0 : }
541 :
542 : } // namespace layers
543 : } // namespace mozilla
|