summaryrefslogtreecommitdiffstats
path: root/gfx/layers/mlgpu/FrameBuilder.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--gfx/layers/mlgpu/FrameBuilder.cpp412
1 files changed, 412 insertions, 0 deletions
diff --git a/gfx/layers/mlgpu/FrameBuilder.cpp b/gfx/layers/mlgpu/FrameBuilder.cpp
new file mode 100644
index 0000000000..adb4e8425c
--- /dev/null
+++ b/gfx/layers/mlgpu/FrameBuilder.cpp
@@ -0,0 +1,412 @@
+/* -*- 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 "FrameBuilder.h"
+#include "ContainerLayerMLGPU.h"
+#include "GeckoProfiler.h" // for profiler_*
+#include "LayerMLGPU.h"
+#include "LayerManagerMLGPU.h"
+#include "MaskOperation.h"
+#include "MLGDevice.h" // for MLGSwapChain
+#include "RenderPassMLGPU.h"
+#include "RenderViewMLGPU.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/gfx/Polygon.h"
+#include "mozilla/layers/BSPTree.h"
+#include "mozilla/layers/LayersHelpers.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mlg;
+
+FrameBuilder::FrameBuilder(LayerManagerMLGPU* aManager,
+ MLGSwapChain* aSwapChain)
+ : mManager(aManager),
+ mDevice(aManager->GetDevice()),
+ mSwapChain(aSwapChain) {
+ // test_bug1124898.html has a root ColorLayer, so we don't assume the root is
+ // a container.
+ mRoot = mManager->GetRoot()->AsHostLayer()->AsLayerMLGPU();
+}
+
+FrameBuilder::~FrameBuilder() = default;
+
+bool FrameBuilder::Build() {
+ AUTO_PROFILER_LABEL("FrameBuilder::Build", GRAPHICS);
+
+ // AcquireBackBuffer can fail, so we check the result here.
+ RefPtr<MLGRenderTarget> target = mSwapChain->AcquireBackBuffer();
+ if (!target) {
+ return false;
+ }
+
+ // This updates the frame sequence number, so layers can quickly check if
+ // they've already been prepared.
+ LayerMLGPU::BeginFrame();
+
+ // Note: we don't clip draw calls to the invalid region per se, but instead
+ // the region bounds. Clipping all draw calls would incur a significant
+ // CPU cost on large layer trees, and would greatly complicate how draw
+ // rects are added in RenderPassMLGPU, since we would need to break
+ // each call into additional items based on the intersection with the
+ // invalid region.
+ //
+ // Instead we scissor to the invalid region bounds. As a result, all items
+ // affecting the invalid bounds are redrawn, even if not all are in the
+ // precise region.
+ const nsIntRegion& region = mSwapChain->GetBackBufferInvalidRegion();
+
+ mWidgetRenderView = new RenderViewMLGPU(this, target, region);
+
+ // Traverse the layer tree and compute visible region for intermediate
+ // surfaces
+ if (ContainerLayerMLGPU* root =
+ mRoot->AsLayerMLGPU()->AsContainerLayerMLGPU()) {
+ root->ComputeIntermediateSurfaceBounds();
+ }
+
+ // Traverse the layer tree and assign each layer to tiles.
+ {
+ Maybe<gfx::Polygon> geometry;
+ RenderTargetIntRect clip(0, 0, target->GetSize().width,
+ target->GetSize().height);
+
+ AssignLayer(mRoot->GetLayer(), mWidgetRenderView, clip,
+ std::move(geometry));
+ }
+
+ // Build the default mask buffer.
+ {
+ MaskInformation defaultMaskInfo(1.0f, false);
+ if (!mDevice->GetSharedPSBuffer()->Allocate(&mDefaultMaskInfo,
+ defaultMaskInfo)) {
+ return false;
+ }
+ }
+
+ // Build render passes and buffer information for each pass.
+ mWidgetRenderView->FinishBuilding();
+ mWidgetRenderView->Prepare();
+
+ // Prepare masks that need to be combined.
+ for (const auto& pair : mCombinedTextureMasks) {
+ pair.second->PrepareForRendering();
+ }
+
+ FinishCurrentLayerBuffer();
+ FinishCurrentMaskRectBuffer();
+ return true;
+}
+
+void FrameBuilder::Render() {
+ AUTO_PROFILER_LABEL("FrameBuilder::Render", GRAPHICS);
+
+ // Render combined masks into single mask textures.
+ for (const auto& pair : mCombinedTextureMasks) {
+ pair.second->Render();
+ }
+
+ // Render to all targets, front-to-back.
+ mWidgetRenderView->Render();
+}
+
+void FrameBuilder::AssignLayer(Layer* aLayer, RenderViewMLGPU* aView,
+ const RenderTargetIntRect& aClipRect,
+ Maybe<gfx::Polygon>&& aGeometry) {
+ LayerMLGPU* layer = aLayer->AsHostLayer()->AsLayerMLGPU();
+
+ if (ContainerLayer* container = aLayer->AsContainerLayer()) {
+ // This returns false if we don't need to (or can't) process the layer any
+ // further. This always returns false for non-leaf ContainerLayers.
+ if (!ProcessContainerLayer(container, aView, aClipRect, aGeometry)) {
+ return;
+ }
+ } else {
+ // Set the precomputed clip and any textures/resources that are needed.
+ if (!layer->PrepareToRender(this, aClipRect)) {
+ return;
+ }
+ }
+
+ // If we are dealing with a nested 3D context, we might need to transform
+ // the geometry back to the coordinate space of the current layer.
+ if (aGeometry) {
+ TransformLayerGeometry(aLayer, aGeometry);
+ }
+
+ // Finally, assign the layer to a rendering batch in the current render
+ // target.
+ layer->AssignToView(this, aView, std::move(aGeometry));
+}
+
+bool FrameBuilder::ProcessContainerLayer(ContainerLayer* aContainer,
+ RenderViewMLGPU* aView,
+ const RenderTargetIntRect& aClipRect,
+ Maybe<gfx::Polygon>& aGeometry) {
+ LayerMLGPU* layer = aContainer->AsHostLayer()->AsLayerMLGPU();
+
+ // Diagnostic information for bug 1387467.
+ if (!layer) {
+ gfxDevCrash(gfx::LogReason::InvalidLayerType)
+ << "Layer type is invalid: " << aContainer->Name();
+ return false;
+ }
+
+ // We don't want to traverse containers twice, so we only traverse them if
+ // they haven't been prepared yet.
+ bool isFirstVisit = !layer->IsPrepared();
+ if (isFirstVisit && !layer->PrepareToRender(this, aClipRect)) {
+ return false;
+ }
+
+ if (!aContainer->UseIntermediateSurface()) {
+ // In case the layer previously required an intermediate surface, we
+ // clear any intermediate render targets here.
+ layer->ClearCachedResources();
+
+ // This is a pass-through container, so we just process children and
+ // instruct AssignLayer to early-return.
+ ProcessChildList(aContainer, aView, aClipRect, aGeometry);
+ return false;
+ }
+
+ // If this is the first visit of the container this frame, and the
+ // container has an unpainted area, we traverse the container. Note that
+ // RefLayers do not have intermediate surfaces so this is guaranteed
+ // to be a full-fledged ContainerLayerMLGPU.
+ ContainerLayerMLGPU* viewContainer = layer->AsContainerLayerMLGPU();
+ if (!viewContainer) {
+ gfxDevCrash(gfx::LogReason::InvalidLayerType)
+ << "Container layer type is invalid: " << aContainer->Name();
+ return false;
+ }
+
+ if (isFirstVisit && !viewContainer->GetInvalidRect().IsEmpty()) {
+ // The RenderView constructor automatically attaches itself to the parent.
+ RefPtr<RenderViewMLGPU> view =
+ new RenderViewMLGPU(this, viewContainer, aView);
+ ProcessChildList(aContainer, view, aClipRect, Nothing());
+ view->FinishBuilding();
+ }
+ return true;
+}
+
+void FrameBuilder::ProcessChildList(
+ ContainerLayer* aContainer, RenderViewMLGPU* aView,
+ const RenderTargetIntRect& aParentClipRect,
+ const Maybe<gfx::Polygon>& aParentGeometry) {
+ nsTArray<LayerPolygon> polygons = aContainer->SortChildrenBy3DZOrder(
+ ContainerLayer::SortMode::WITH_GEOMETRY);
+
+ // Visit layers in front-to-back order.
+ for (auto iter = polygons.rbegin(); iter != polygons.rend(); iter++) {
+ LayerPolygon& entry = *iter;
+ Layer* child = entry.layer;
+ if (child->IsBackfaceHidden() || !child->IsVisible()) {
+ continue;
+ }
+
+ RenderTargetIntRect clip = child->CalculateScissorRect(aParentClipRect);
+ if (clip.IsEmpty()) {
+ continue;
+ }
+
+ Maybe<gfx::Polygon> geometry;
+ if (aParentGeometry && entry.geometry) {
+ // Both parent and child are split.
+ geometry = Some(aParentGeometry->ClipPolygon(*entry.geometry));
+ } else if (aParentGeometry) {
+ geometry = aParentGeometry;
+ } else if (entry.geometry) {
+ geometry = std::move(entry.geometry);
+ }
+
+ AssignLayer(child, aView, clip, std::move(geometry));
+ }
+}
+
+bool FrameBuilder::AddLayerToConstantBuffer(ItemInfo& aItem) {
+ LayerMLGPU* layer = aItem.layer;
+
+ // If this layer could appear multiple times, cache it.
+ if (aItem.geometry) {
+ if (mLayerBufferMap.Get(layer, &aItem.layerIndex)) {
+ return true;
+ }
+ }
+
+ LayerConstants* info = AllocateLayerInfo(aItem);
+ if (!info) {
+ return false;
+ }
+
+ // Note we do not use GetEffectiveTransformForBuffer, since we calculate
+ // the correct scaling when we build texture coordinates.
+ Layer* baseLayer = layer->GetLayer();
+ const gfx::Matrix4x4& transform = baseLayer->GetEffectiveTransform();
+
+ memcpy(&info->transform, &transform._11, 64);
+ info->clipRect = gfx::Rect(layer->GetComputedClipRect().ToUnknownRect());
+ info->maskIndex = 0;
+ if (MaskOperation* op = layer->GetMask()) {
+ // Note: we use 0 as an invalid index, and so indices are offset by 1.
+ gfx::Rect rect = op->ComputeMaskRect(baseLayer);
+ AddMaskRect(rect, &info->maskIndex);
+ }
+
+ if (aItem.geometry) {
+ mLayerBufferMap.Put(layer, aItem.layerIndex);
+ }
+ return true;
+}
+
+MaskOperation* FrameBuilder::AddMaskOperation(LayerMLGPU* aLayer) {
+ Layer* layer = aLayer->GetLayer();
+ MOZ_ASSERT(layer->HasMaskLayers());
+
+ // Multiple masks are combined into a single mask.
+ if ((layer->GetMaskLayer() && layer->GetAncestorMaskLayerCount()) ||
+ layer->GetAncestorMaskLayerCount() > 1) {
+ // Since each mask can be moved independently of the other, we must create
+ // a separate combined mask for every new positioning we encounter.
+ MaskTextureList textures;
+ if (Layer* maskLayer = layer->GetMaskLayer()) {
+ AppendToMaskTextureList(textures, maskLayer);
+ }
+ for (size_t i = 0; i < layer->GetAncestorMaskLayerCount(); i++) {
+ AppendToMaskTextureList(textures, layer->GetAncestorMaskLayerAt(i));
+ }
+
+ auto iter = mCombinedTextureMasks.find(textures);
+ if (iter != mCombinedTextureMasks.end()) {
+ return iter->second;
+ }
+
+ RefPtr<MaskCombineOperation> op = new MaskCombineOperation(this);
+ op->Init(textures);
+
+ mCombinedTextureMasks[textures] = op;
+ return op;
+ }
+
+ Layer* maskLayer = layer->GetMaskLayer() ? layer->GetMaskLayer()
+ : layer->GetAncestorMaskLayerAt(0);
+ RefPtr<TextureSource> texture = GetMaskLayerTexture(maskLayer);
+ if (!texture) {
+ return nullptr;
+ }
+
+ RefPtr<MaskOperation> op;
+ mSingleTextureMasks.Get(texture, getter_AddRefs(op));
+ if (op) {
+ return op;
+ }
+
+ RefPtr<MLGTexture> wrapped = mDevice->CreateTexture(texture);
+
+ op = new MaskOperation(this, wrapped);
+ mSingleTextureMasks.Put(texture, RefPtr{op});
+ return op;
+}
+
+void FrameBuilder::RetainTemporaryLayer(LayerMLGPU* aLayer) {
+ // This should only be used with temporary layers. Temporary layers do not
+ // have parents.
+ MOZ_ASSERT(!aLayer->GetLayer()->GetParent());
+ mTemporaryLayers.push_back(aLayer->GetLayer());
+}
+
+MLGRenderTarget* FrameBuilder::GetWidgetRT() {
+ return mWidgetRenderView->GetRenderTarget();
+}
+
+LayerConstants* FrameBuilder::AllocateLayerInfo(ItemInfo& aItem) {
+ if (((mCurrentLayerBuffer.Length() + 1) * sizeof(LayerConstants)) >
+ mDevice->GetMaxConstantBufferBindSize()) {
+ FinishCurrentLayerBuffer();
+ mLayerBufferMap.Clear();
+ mCurrentLayerBuffer.ClearAndRetainStorage();
+ }
+
+ LayerConstants* info = mCurrentLayerBuffer.AppendElement(mozilla::fallible);
+ if (!info) {
+ return nullptr;
+ }
+
+ aItem.layerIndex = mCurrentLayerBuffer.Length() - 1;
+ return info;
+}
+
+void FrameBuilder::FinishCurrentLayerBuffer() {
+ if (mCurrentLayerBuffer.IsEmpty()) {
+ return;
+ }
+
+ // Note: we append the buffer even if we couldn't allocate one, since
+ // that keeps the indices sane.
+ ConstantBufferSection section;
+ mDevice->GetSharedVSBuffer()->Allocate(
+ &section, mCurrentLayerBuffer.Elements(), mCurrentLayerBuffer.Length());
+ mLayerBuffers.AppendElement(section);
+}
+
+size_t FrameBuilder::CurrentLayerBufferIndex() const {
+ // The mask rect buffer list doesn't contain the buffer currently being
+ // built, so we don't subtract 1 here.
+ return mLayerBuffers.Length();
+}
+
+ConstantBufferSection FrameBuilder::GetLayerBufferByIndex(size_t aIndex) const {
+ if (aIndex >= mLayerBuffers.Length()) {
+ return ConstantBufferSection();
+ }
+ return mLayerBuffers[aIndex];
+}
+
+bool FrameBuilder::AddMaskRect(const gfx::Rect& aRect, uint32_t* aOutIndex) {
+ if (((mCurrentMaskRectList.Length() + 1) * sizeof(gfx::Rect)) >
+ mDevice->GetMaxConstantBufferBindSize()) {
+ FinishCurrentMaskRectBuffer();
+ mCurrentMaskRectList.ClearAndRetainStorage();
+ }
+
+ mCurrentMaskRectList.AppendElement(aRect);
+
+ // Mask indices start at 1 so the shader can use 0 as a no-mask indicator.
+ *aOutIndex = mCurrentMaskRectList.Length();
+ return true;
+}
+
+void FrameBuilder::FinishCurrentMaskRectBuffer() {
+ if (mCurrentMaskRectList.IsEmpty()) {
+ return;
+ }
+
+ // Note: we append the buffer even if we couldn't allocate one, since
+ // that keeps the indices sane.
+ ConstantBufferSection section;
+ mDevice->GetSharedVSBuffer()->Allocate(
+ &section, mCurrentMaskRectList.Elements(), mCurrentMaskRectList.Length());
+ mMaskRectBuffers.AppendElement(section);
+}
+
+size_t FrameBuilder::CurrentMaskRectBufferIndex() const {
+ // The mask rect buffer list doesn't contain the buffer currently being
+ // built, so we don't subtract 1 here.
+ return mMaskRectBuffers.Length();
+}
+
+ConstantBufferSection FrameBuilder::GetMaskRectBufferByIndex(
+ size_t aIndex) const {
+ if (aIndex >= mMaskRectBuffers.Length()) {
+ return ConstantBufferSection();
+ }
+ return mMaskRectBuffers[aIndex];
+}
+
+} // namespace layers
+} // namespace mozilla