From 3df14ab59d30a9323feaaa7823bbf929519c98a0 Mon Sep 17 00:00:00 2001 From: Tim Sylvester Date: Thu, 16 Nov 2023 09:33:29 -0800 Subject: [PATCH] Bug/stencil tiles (#1871) --- include/mbgl/renderer/layer_group.hpp | 8 +++ src/mbgl/gl/layer_group_gl.cpp | 29 ++++---- src/mbgl/mtl/tile_layer_group.cpp | 40 ++++------- .../layers/render_background_layer.cpp | 12 ++-- .../layers/render_fill_extrusion_layer.cpp | 3 +- .../renderer/layers/render_fill_layer.cpp | 12 ++-- .../renderer/layers/render_heatmap_layer.cpp | 2 - .../renderer/layers/render_line_layer.cpp | 2 + .../renderer/layers/render_symbol_layer.cpp | 2 - src/mbgl/renderer/paint_parameters.cpp | 69 ++++++------------- src/mbgl/renderer/paint_parameters.hpp | 7 -- .../renderer/sources/render_tile_source.cpp | 1 - src/mbgl/renderer/tile_layer_group.cpp | 4 ++ .../style/layers/custom_drawable_layer.cpp | 1 - 14 files changed, 77 insertions(+), 115 deletions(-) diff --git a/include/mbgl/renderer/layer_group.hpp b/include/mbgl/renderer/layer_group.hpp index 8780ba8d3..8a7726ea1 100644 --- a/include/mbgl/renderer/layer_group.hpp +++ b/include/mbgl/renderer/layer_group.hpp @@ -13,10 +13,12 @@ namespace mbgl { class LayerGroupBase; class PaintParameters; class RenderOrchestrator; +class RenderTile; class RenderTree; class TileLayerGroup; using LayerGroupBasePtr = std::shared_ptr; +using RenderTiles = std::shared_ptr>>; namespace gfx { class Context; @@ -127,9 +129,15 @@ public: std::size_t clearDrawables() override; + void setStencilTiles(RenderTiles); + protected: struct Impl; std::unique_ptr impl; + + // When stencil clipping is enabled for the layer, this is the set + // of tile IDs that need to be rendered to the stencil buffer. + RenderTiles stencilTiles; }; /** diff --git a/src/mbgl/gl/layer_group_gl.cpp b/src/mbgl/gl/layer_group_gl.cpp index 0ce9a50e6..be1a993ea 100644 --- a/src/mbgl/gl/layer_group_gl.cpp +++ b/src/mbgl/gl/layer_group_gl.cpp @@ -62,22 +62,17 @@ void TileLayerGroupGL::render(RenderOrchestrator&, PaintParameters& parameters) const auto debugGroupClip = parameters.encoder->createDebugGroup(label_clip.c_str()); #endif - // Collect the tile IDs relevant to stenciling and update the stencil buffer, if necessary. - std::set tileIDs; - visitDrawables([&](const gfx::Drawable& drawable) { - if (!drawable.getEnabled() || !drawable.hasRenderPass(parameters.pass)) { - return; - } - if (drawable.getIs3D()) { - features3d = true; - if (drawable.getEnableStencil()) { - stencil3d = true; + // If we're using stencil clipping, we need to handle 3D features separately + if (stencilTiles && !stencilTiles->empty()) { + visitDrawables([&](const gfx::Drawable& drawable) { + if (drawable.getEnabled() && drawable.getIs3D() && drawable.hasRenderPass(parameters.pass)) { + features3d = true; + if (drawable.getEnableStencil()) { + stencil3d = true; + } } - } - if (!features3d && drawable.getEnableStencil() && drawable.getTileID()) { - tileIDs.emplace(drawable.getTileID()->toUnwrapped()); - } - }); + }); + } // If we're doing 3D stenciling and have any features // to draw, set up the single-value stencil mask. @@ -85,8 +80,8 @@ void TileLayerGroupGL::render(RenderOrchestrator&, PaintParameters& parameters) // render each tile into the stencil buffer with a different value. if (features3d) { stencilMode3d = stencil3d ? parameters.stencilModeFor3D() : gfx::StencilMode::disabled(); - } else if (!tileIDs.empty()) { - parameters.renderTileClippingMasks(tileIDs); + } else if (stencilTiles && !stencilTiles->empty()) { + parameters.renderTileClippingMasks(stencilTiles); } } diff --git a/src/mbgl/mtl/tile_layer_group.cpp b/src/mbgl/mtl/tile_layer_group.cpp index 17e237d70..e2183aeb8 100644 --- a/src/mbgl/mtl/tile_layer_group.cpp +++ b/src/mbgl/mtl/tile_layer_group.cpp @@ -55,37 +55,25 @@ void TileLayerGroup::render(RenderOrchestrator&, PaintParameters& parameters) { bool stencil3d = false; gfx::StencilMode stencilMode3d; - // Collect the tile IDs relevant to stenciling and update the stencil buffer, if necessary. - std::set tileIDs; - std::size_t numEnabled = 0; - visitDrawables([&](const gfx::Drawable& drawable) { - if (!drawable.getEnabled() || !drawable.hasRenderPass(parameters.pass)) { - return; - } - numEnabled += 1; - if (drawable.getIs3D()) { - features3d = true; - if (drawable.getEnableStencil()) { - stencil3d = true; + // If we're using stencil clipping, we need to handle 3D features separately + if (stencilTiles && !stencilTiles->empty()) { + visitDrawables([&](const gfx::Drawable& drawable) { + if (drawable.getEnabled() && drawable.getIs3D() && drawable.hasRenderPass(parameters.pass)) { + features3d = true; + if (drawable.getEnableStencil()) { + stencil3d = true; + } } - } - if (!features3d && drawable.getEnableStencil() && drawable.getTileID()) { - tileIDs.emplace(drawable.getTileID()->toUnwrapped()); - } - }); - - if (!numEnabled) { - return; + }); } #if !defined(NDEBUG) const auto debugGroupRender = parameters.encoder->createDebugGroup(getName() + "-render"); #endif - // If we're doing 3D stenciling and have any features - // to draw, set up the single-value stencil mask. - // If we're doing 2D stenciling and have any drawables with tile IDs, - // render each tile into the stencil buffer with a different value. + // If we're doing 3D stenciling and have any features to draw, set up the single-value stencil mask. + // If we're doing 2D stenciling and have any drawables with tile IDs, render each tile into the stencil buffer with + // a different value. MTLDepthStencilStatePtr stateWithStencil, stateWithoutStencil; if (features3d) { const auto depthMode = parameters.depthModeFor3D(); @@ -95,8 +83,8 @@ void TileLayerGroup::render(RenderOrchestrator&, PaintParameters& parameters) { encoder->setStencilReferenceValue(stencilMode3d.ref); } stateWithoutStencil = context.makeDepthStencilState(depthMode, gfx::StencilMode::disabled(), renderable); - } else if (!tileIDs.empty()) { - parameters.renderTileClippingMasks(tileIDs); + } else if (stencilTiles && !stencilTiles->empty()) { + parameters.renderTileClippingMasks(stencilTiles); } visitDrawables([&](gfx::Drawable& drawable) { diff --git a/src/mbgl/renderer/layers/render_background_layer.cpp b/src/mbgl/renderer/layers/render_background_layer.cpp index 42e41432f..dd148cbb5 100644 --- a/src/mbgl/renderer/layers/render_background_layer.cpp +++ b/src/mbgl/renderer/layers/render_background_layer.cpp @@ -204,10 +204,6 @@ void RenderBackgroundLayer::prepare(const LayerPrepareParameters& params) { addPatternIfNeeded(evaluated.get().from.id(), params); addPatternIfNeeded(evaluated.get().to.id(), params); } - -#if MLN_DRAWABLE_RENDERER - updateRenderTileIDs(); -#endif // MLN_DRAWABLE_RENDERER } #if MLN_DRAWABLE_RENDERER @@ -290,9 +286,11 @@ void RenderBackgroundLayer::update(gfx::ShaderRegistry& shaders, std::unique_ptr builder; - tileLayerGroup->visitDrawables([&](gfx::Drawable& drawable) -> bool { - // Has this tile dropped out of the cover set? - return (!drawable.getTileID() || hasRenderTile(*drawable.getTileID())); + // Remove drawables for tiles that are no longer in the cover set. + // (Note that `RenderTiles` is empty, and this layer does not use it) + tileLayerGroup->removeDrawablesIf([&](gfx::Drawable& drawable) -> bool { + return drawable.getTileID() && + (std::find(tileCover.begin(), tileCover.end(), *drawable.getTileID()) == tileCover.end()); }); // For each tile in the cover set, add a tile drawable if one doesn't already exist. diff --git a/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp index 5f01444e9..e4cf026c5 100644 --- a/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp +++ b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp @@ -347,6 +347,8 @@ void RenderFillExtrusionLayer::update(gfx::ShaderRegistry& shaders, return; } + tileLayerGroup->setStencilTiles(renderTiles); + std::unordered_set propertiesAsUniforms; for (const RenderTile& tile : *renderTiles) { const auto& tileID = tile.getOverscaledTileID(); @@ -448,7 +450,6 @@ void RenderFillExtrusionLayer::update(gfx::ShaderRegistry& shaders, if (auto builder = context.createDrawableBuilder(layerPrefix + "depth")) { builder->setShader(shader); builder->setIs3D(true); - builder->setEnableStencil(false); builder->setEnableColor(false); builder->setRenderPass(drawPass); builder->setCullFaceMode(gfx::CullFaceMode::backCCW()); diff --git a/src/mbgl/renderer/layers/render_fill_layer.cpp b/src/mbgl/renderer/layers/render_fill_layer.cpp index ce70b1f20..e57a05cb9 100644 --- a/src/mbgl/renderer/layers/render_fill_layer.cpp +++ b/src/mbgl/renderer/layers/render_fill_layer.cpp @@ -364,6 +364,8 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders, return tileID && !hasRenderTile(*tileID); }); + tileLayerGroup->setStencilTiles(renderTiles); + std::unordered_set propertiesAsUniforms; for (const RenderTile& tile : *renderTiles) { const auto& tileID = tile.getOverscaledTileID(); @@ -559,11 +561,13 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders, if (!fillBuilder && fillShader) { if (auto builder = context.createDrawableBuilder(layerPrefix + "fill")) { + // Only write opaque fills to the depth buffer, matching `fillRenderPass` in legacy rendering + const bool opaque = (evaluated.get().constantOr(Color()).a >= 1.0f && + evaluated.get().constantOr(0) >= 1.0f); + commonInit(*builder); - builder->setDepthType((renderPass == RenderPass::Opaque) ? gfx::DepthMaskType::ReadWrite - : gfx::DepthMaskType::ReadOnly); - builder->setColorMode(renderPass == RenderPass::Translucent ? gfx::ColorMode::alphaBlended() - : gfx::ColorMode::unblended()); + builder->setDepthType(opaque ? gfx::DepthMaskType::ReadWrite : gfx::DepthMaskType::ReadOnly); + builder->setColorMode(opaque ? gfx::ColorMode::unblended() : gfx::ColorMode::alphaBlended()); builder->setSubLayerIndex(1); builder->setRenderPass(renderPass); fillBuilder = std::move(builder); diff --git a/src/mbgl/renderer/layers/render_heatmap_layer.cpp b/src/mbgl/renderer/layers/render_heatmap_layer.cpp index cdfce80b0..d23ed86f2 100644 --- a/src/mbgl/renderer/layers/render_heatmap_layer.cpp +++ b/src/mbgl/renderer/layers/render_heatmap_layer.cpp @@ -435,7 +435,6 @@ void RenderHeatmapLayer::update(gfx::ShaderRegistry& shaders, heatmapBuilder->setEnableDepth(false); heatmapBuilder->setColorMode(gfx::ColorMode::additive()); heatmapBuilder->setCullFaceMode(gfx::CullFaceMode::disabled()); - heatmapBuilder->setEnableStencil(false); heatmapBuilder->setRenderPass(renderPass); heatmapBuilder->setVertexAttributes(std::move(heatmapVertexAttrs)); heatmapBuilder->setRawVertices({}, vertexCount, gfx::AttributeDataType::Short2); @@ -509,7 +508,6 @@ void RenderHeatmapLayer::update(gfx::ShaderRegistry& shaders, heatmapTextureBuilder->setEnableDepth(false); heatmapTextureBuilder->setColorMode(gfx::ColorMode::alphaBlended()); heatmapTextureBuilder->setCullFaceMode(gfx::CullFaceMode::disabled()); - heatmapTextureBuilder->setEnableStencil(false); heatmapTextureBuilder->setRenderPass(renderPass); heatmapTextureBuilder->setVertexAttributes(std::move(textureVertexAttrs)); heatmapTextureBuilder->setRawVertices({}, textureVertexCount, gfx::AttributeDataType::Short2); diff --git a/src/mbgl/renderer/layers/render_line_layer.cpp b/src/mbgl/renderer/layers/render_line_layer.cpp index 162bfb90d..8bec28b40 100644 --- a/src/mbgl/renderer/layers/render_line_layer.cpp +++ b/src/mbgl/renderer/layers/render_line_layer.cpp @@ -461,6 +461,8 @@ void RenderLineLayer::update(gfx::ShaderRegistry& shaders, builder->setSegments(gfx::Triangles(), bucket.sharedTriangles, bucket.segments.data(), bucket.segments.size()); }; + tileLayerGroup->setStencilTiles(renderTiles); + std::unordered_set propertiesAsUniforms; for (const RenderTile& tile : *renderTiles) { const auto& tileID = tile.getOverscaledTileID(); diff --git a/src/mbgl/renderer/layers/render_symbol_layer.cpp b/src/mbgl/renderer/layers/render_symbol_layer.cpp index cd365fd55..2b0137047 100644 --- a/src/mbgl/renderer/layers/render_symbol_layer.cpp +++ b/src/mbgl/renderer/layers/render_symbol_layer.cpp @@ -1055,7 +1055,6 @@ void RenderSymbolLayer::update(gfx::ShaderRegistry& shaders, std::unique_ptr collisionBuilder = context.createDrawableBuilder(layerCollisionPrefix); collisionBuilder->setSubLayerIndex(0); - collisionBuilder->setEnableStencil(false); collisionBuilder->setEnableDepth(false); collisionBuilder->setRenderPass(passes); collisionBuilder->setCullFaceMode(gfx::CullFaceMode::disabled()); @@ -1316,7 +1315,6 @@ void RenderSymbolLayer::update(gfx::ShaderRegistry& shaders, if (!builder) { builder = context.createDrawableBuilder(layerPrefix); builder->setSubLayerIndex(0); - builder->setEnableStencil(false); builder->setRenderPass(passes); builder->setCullFaceMode(gfx::CullFaceMode::disabled()); builder->setDepthType(gfx::DepthMaskType::ReadOnly); diff --git a/src/mbgl/renderer/paint_parameters.cpp b/src/mbgl/renderer/paint_parameters.cpp index 158975619..cd9841da1 100644 --- a/src/mbgl/renderer/paint_parameters.cpp +++ b/src/mbgl/renderer/paint_parameters.cpp @@ -101,7 +101,9 @@ using GetTileIDFunc = const UnwrappedTileID& (*)(const typename TIter::value_typ using TileMaskIDMap = std::map; -// `std::includes` adapted to extract tile IDs from arbitrary collection iterators +// `std::includes` adapted to extract tile IDs from arbitrary collection iterators. +// This is needed because, although it accepts iterators of different types, they must be: +// "...such that an object of type InputIt can be dereferenced and then implicitly converted to both of them.​" template bool includes(I1 beg1, const I1 end1, I2 beg2, const I2 end2, const GetTileIDFunc& unwrap) { for (; beg2 != end2; ++beg1) { @@ -118,25 +120,20 @@ bool includes(I1 beg1, const I1 end1, I2 beg2, const I2 end2, const GetTileIDFun return true; } -// Check whether the given set of tile IDs is a subset of the ones already rendered -template -bool tileIDsCovered(TIter beg, TIter end, GetTileIDFunc& unwrap, const TileMaskIDMap& idMap) { - if (idMap.size() < static_cast(std::distance(beg, end))) { - return false; - } - assert(std::is_sorted(beg, end, [=](const auto& a, const auto& b) { return unwrap(a) < unwrap(b); })); - return includes(idMap.cbegin(), idMap.cend(), beg, end, unwrap); -} - -#if MLN_LEGACY_RENDERER -const UnwrappedTileID& unwrapRenderTiles(const RenderTiles::element_type::value_type& iter) { +const UnwrappedTileID& unwrap(const RenderTiles::element_type::value_type& iter) { return iter.get().id; } -#else // !MLN_LEGACY_RENDERER -const UnwrappedTileID& unwrapIdentity(const UnwrappedTileID& value) { - return value; + +// Check whether the given set of tile IDs is a subset of the ones already rendered +bool tileIDsCovered(const RenderTiles& tiles, const TileMaskIDMap& idMap) { + if (idMap.size() < tiles->size()) { + return false; + } + assert(std::is_sorted( + tiles->begin(), tiles->end(), [=](const auto& a, const auto& b) { return unwrap(a) < unwrap(b); })); + return includes(idMap.cbegin(), idMap.cend(), tiles->cbegin(), tiles->cend(), &unwrap); } -#endif // MLN_LEGACY_RENDERER + } // namespace void PaintParameters::clearStencil() { @@ -164,35 +161,13 @@ void PaintParameters::clearStencil() { #endif } -#if MLN_LEGACY_RENDERER void PaintParameters::renderTileClippingMasks(const RenderTiles& renderTiles) { - renderTileClippingMasks((*renderTiles).cbegin(), (*renderTiles).cend(), &unwrapRenderTiles); -} -#else // !MLN_LEGACY_RENDERER -void PaintParameters::renderTileClippingMasks(const std::set& tileIDs) { - renderTileClippingMasks(tileIDs.cbegin(), tileIDs.cend(), &unwrapIdentity); -} -#endif // MLN_LEGACY_RENDERER - -void PaintParameters::clearTileClippingMasks() { - if (!tileClippingMaskIDs.empty()) { - clearStencil(); - } -} - -template -void PaintParameters::renderTileClippingMasks(TIter beg, TIter end, GetTileIDFunc unwrap) { - if (!renderPass) { - assert(false); + // If the current stencil mask covers this source already, there's no need to draw another one. + if (!renderTiles || !renderPass || tileIDsCovered(renderTiles, tileClippingMaskIDs)) { return; } - if (tileIDsCovered(beg, end, unwrap, tileClippingMaskIDs)) { - // The current stencil mask is for this source already; no need to draw another one. - return; - } - - const auto count = std::distance(beg, end); + const auto count = renderTiles->size(); if (nextStencilID + count > maxStencilValue) { // we'll run out of fresh IDs so we need to clear and start from scratch clearStencil(); @@ -201,8 +176,8 @@ void PaintParameters::renderTileClippingMasks(TIter beg, TIter end, GetTileIDFun #if MLN_RENDER_BACKEND_METAL // Assign a stencil ID and build a UBO for each tile in the set std::vector tileUBOs; - for (auto i = beg; i != end; ++i) { - const auto& tileID = unwrap(*i); + for (const auto& tileRef : *renderTiles) { + const auto& tileID = tileRef.get().id; const int32_t stencilID = nextStencilID; const auto result = tileClippingMaskIDs.insert(std::make_pair(tileID, stencilID)); @@ -215,7 +190,7 @@ void PaintParameters::renderTileClippingMasks(TIter beg, TIter end, GetTileIDFun } if (tileUBOs.empty()) { - tileUBOs.reserve(std::distance(beg, end)); + tileUBOs.reserve(count); } tileUBOs.emplace_back(shaders::ClipUBO{/*.matrix=*/util::cast(matrixForTile(tileID)), @@ -248,8 +223,8 @@ void PaintParameters::renderTileClippingMasks(TIter beg, TIter end, GetTileIDFun const style::Properties<>::PossiblyEvaluated properties{}; const ClippingMaskProgram::Binders paintAttributeData(properties, 0); - for (auto i = beg; i != end; ++i) { - const auto& tileID = unwrap(*i); + for (const auto& tileRef : *renderTiles) { + const auto& tileID = tileRef.get().id; const int32_t stencilID = nextStencilID; const auto result = tileClippingMaskIDs.insert(std::make_pair(tileID, stencilID)); diff --git a/src/mbgl/renderer/paint_parameters.hpp b/src/mbgl/renderer/paint_parameters.hpp index c42fdc0ca..d2bab3987 100644 --- a/src/mbgl/renderer/paint_parameters.hpp +++ b/src/mbgl/renderer/paint_parameters.hpp @@ -95,14 +95,7 @@ public: // Stencil handling public: -#if MLN_LEGACY_RENDERER void renderTileClippingMasks(const RenderTiles&); -#else - void renderTileClippingMasks(const std::set&); -#endif // MLN_LEGACY_RENDERER - - /// Clear any tile masks and the stencil buffer, if necessary - void clearTileClippingMasks(); /// Clear the stencil buffer, even if there are no tile masks (for 3D) void clearStencil(); diff --git a/src/mbgl/renderer/sources/render_tile_source.cpp b/src/mbgl/renderer/sources/render_tile_source.cpp index d0e1f18ea..55b19622b 100644 --- a/src/mbgl/renderer/sources/render_tile_source.cpp +++ b/src/mbgl/renderer/sources/render_tile_source.cpp @@ -68,7 +68,6 @@ void TileSourceRenderItem::updateDebugDrawables(DebugLayerGroupMap& debugLayerGr builder->setEnableDepth(false); builder->setColorMode(gfx::ColorMode::unblended()); builder->setCullFaceMode(gfx::CullFaceMode::disabled()); - builder->setEnableStencil(false); builder->setVertexAttrNameId(idVertexAttribName); // add or get the layer group for a debug type diff --git a/src/mbgl/renderer/tile_layer_group.cpp b/src/mbgl/renderer/tile_layer_group.cpp index 24e5820af..e79263605 100644 --- a/src/mbgl/renderer/tile_layer_group.cpp +++ b/src/mbgl/renderer/tile_layer_group.cpp @@ -142,4 +142,8 @@ std::size_t TileLayerGroup::clearDrawables() { return count; } +void TileLayerGroup::setStencilTiles(RenderTiles tiles) { + stencilTiles = std::move(tiles); +} + } // namespace mbgl diff --git a/src/mbgl/style/layers/custom_drawable_layer.cpp b/src/mbgl/style/layers/custom_drawable_layer.cpp index bd08cdaf0..828764f99 100644 --- a/src/mbgl/style/layers/custom_drawable_layer.cpp +++ b/src/mbgl/style/layers/custom_drawable_layer.cpp @@ -395,7 +395,6 @@ std::unique_ptr CustomDrawableLayerHost::Interface::create builder_->setEnableDepth(false); builder_->setColorMode(gfx::ColorMode::alphaBlended()); builder_->setCullFaceMode(gfx::CullFaceMode::disabled()); - builder_->setEnableStencil(false); builder_->setRenderPass(RenderPass::Translucent); return builder_;