Bug/stencil tiles (#1871)
This commit is contained in:
@@ -13,10 +13,12 @@ namespace mbgl {
|
||||
class LayerGroupBase;
|
||||
class PaintParameters;
|
||||
class RenderOrchestrator;
|
||||
class RenderTile;
|
||||
class RenderTree;
|
||||
class TileLayerGroup;
|
||||
|
||||
using LayerGroupBasePtr = std::shared_ptr<LayerGroupBase>;
|
||||
using RenderTiles = std::shared_ptr<const std::vector<std::reference_wrapper<const RenderTile>>>;
|
||||
|
||||
namespace gfx {
|
||||
class Context;
|
||||
@@ -127,9 +129,15 @@ public:
|
||||
|
||||
std::size_t clearDrawables() override;
|
||||
|
||||
void setStencilTiles(RenderTiles);
|
||||
|
||||
protected:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> 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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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<UnwrappedTileID> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<UnwrappedTileID> 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) {
|
||||
|
||||
@@ -204,10 +204,6 @@ void RenderBackgroundLayer::prepare(const LayerPrepareParameters& params) {
|
||||
addPatternIfNeeded(evaluated.get<BackgroundPattern>().from.id(), params);
|
||||
addPatternIfNeeded(evaluated.get<BackgroundPattern>().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<gfx::DrawableBuilder> 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.
|
||||
|
||||
@@ -347,6 +347,8 @@ void RenderFillExtrusionLayer::update(gfx::ShaderRegistry& shaders,
|
||||
return;
|
||||
}
|
||||
|
||||
tileLayerGroup->setStencilTiles(renderTiles);
|
||||
|
||||
std::unordered_set<StringIdentity> 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());
|
||||
|
||||
@@ -364,6 +364,8 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders,
|
||||
return tileID && !hasRenderTile(*tileID);
|
||||
});
|
||||
|
||||
tileLayerGroup->setStencilTiles(renderTiles);
|
||||
|
||||
std::unordered_set<StringIdentity> 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<FillColor>().constantOr(Color()).a >= 1.0f &&
|
||||
evaluated.get<FillOpacity>().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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<StringIdentity> propertiesAsUniforms;
|
||||
for (const RenderTile& tile : *renderTiles) {
|
||||
const auto& tileID = tile.getOverscaledTileID();
|
||||
|
||||
@@ -1055,7 +1055,6 @@ void RenderSymbolLayer::update(gfx::ShaderRegistry& shaders,
|
||||
|
||||
std::unique_ptr<gfx::DrawableBuilder> 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);
|
||||
|
||||
@@ -101,7 +101,9 @@ using GetTileIDFunc = const UnwrappedTileID& (*)(const typename TIter::value_typ
|
||||
|
||||
using TileMaskIDMap = std::map<UnwrappedTileID, int32_t>;
|
||||
|
||||
// `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 <class I1, class I2>
|
||||
bool includes(I1 beg1, const I1 end1, I2 beg2, const I2 end2, const GetTileIDFunc<I2>& 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 <typename TIter>
|
||||
bool tileIDsCovered(TIter beg, TIter end, GetTileIDFunc<TIter>& unwrap, const TileMaskIDMap& idMap) {
|
||||
if (idMap.size() < static_cast<std::size_t>(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<UnwrappedTileID>& tileIDs) {
|
||||
renderTileClippingMasks(tileIDs.cbegin(), tileIDs.cend(), &unwrapIdentity);
|
||||
}
|
||||
#endif // MLN_LEGACY_RENDERER
|
||||
|
||||
void PaintParameters::clearTileClippingMasks() {
|
||||
if (!tileClippingMaskIDs.empty()) {
|
||||
clearStencil();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename TIter>
|
||||
void PaintParameters::renderTileClippingMasks(TIter beg, TIter end, GetTileIDFunc<TIter> 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<shaders::ClipUBO> 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<float>(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));
|
||||
|
||||
@@ -95,14 +95,7 @@ public:
|
||||
|
||||
// Stencil handling
|
||||
public:
|
||||
#if MLN_LEGACY_RENDERER
|
||||
void renderTileClippingMasks(const RenderTiles&);
|
||||
#else
|
||||
void renderTileClippingMasks(const std::set<UnwrappedTileID>&);
|
||||
#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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -142,4 +142,8 @@ std::size_t TileLayerGroup::clearDrawables() {
|
||||
return count;
|
||||
}
|
||||
|
||||
void TileLayerGroup::setStencilTiles(RenderTiles tiles) {
|
||||
stencilTiles = std::move(tiles);
|
||||
}
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -395,7 +395,6 @@ std::unique_ptr<gfx::DrawableBuilder> 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_;
|
||||
|
||||
Reference in New Issue
Block a user