Topic/fill pass (#1225)

This commit is contained in:
Tim Sylvester
2023-06-09 07:23:17 -07:00
committed by Bart Louwers
parent bffa77f98c
commit b060abeb92
15 changed files with 344 additions and 259 deletions

View File

@@ -14,6 +14,7 @@
#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/renderer/render_source.hpp>
#include <mbgl/renderer/render_tile.hpp>
#include <mbgl/renderer/tile_render_data.hpp>
#include <mbgl/style/expression/image.hpp>
#include <mbgl/style/layers/fill_layer_impl.hpp>
#include <mbgl/tile/geometry_tile.hpp>
@@ -22,6 +23,7 @@
#include <mbgl/util/intersection_tests.hpp>
#include <mbgl/util/logging.hpp>
#include <mbgl/util/math.hpp>
#include <mbgl/util/std.hpp>
namespace mbgl {
@@ -277,6 +279,101 @@ void RenderFillLayer::removeTile(RenderPass renderPass, const OverscaledTileID&
stats.tileDrawablesRemoved += tileLayerGroup->removeDrawables(renderPass, tileID).size();
}
using FloatAttr = gfx::AttributeType<float, 1>;
using FloatPairAttr = gfx::AttributeType<float, 2>;
using FloatPair = FloatPairAttr::Value;
using UShortQuadAttr = gfx::AttributeType<uint16_t, 4>;
using UShortQuad = UShortQuadAttr::Value;
using FloatVertex = gfx::detail::VertexType<FloatAttr>;
using FloatPairVertex = gfx::detail::VertexType<FloatPairAttr>;
using UShortQuadVertex = gfx::detail::VertexType<UShortQuadAttr>;
using FloatBinder = PaintPropertyBinder<float, float, mbgl::PossiblyEvaluatedPropertyValue<float>, FloatAttr>;
using FloatPairBinder =
PaintPropertyBinder<FloatPair, FloatPair, mbgl::PossiblyEvaluatedPropertyValue<FloatPair>, FloatPairAttr>;
using ColorBinder = PaintPropertyBinder<Color, Color, mbgl::PossiblyEvaluatedPropertyValue<Color>, FloatPairAttr>;
using ImageBinder = PaintPropertyBinder<style::expression::Image,
UShortQuad,
PossiblyEvaluatedPropertyValue<mbgl::Faded<mbgl::style::expression::Image>>,
UShortQuadAttr,
UShortQuadAttr>;
bool bindFloat(const FloatBinder& binder, gfx::VertexAttributeArray& attrs, std::string attributeName) {
const auto count = binder.getVertexCount();
if (auto& attr = attrs.getOrAdd(std::move(attributeName))) {
for (std::size_t i = 0; i < count; ++i) {
const auto& value = static_cast<const FloatVertex*>(binder.getVertexValue(i))->a1;
const auto& from = value; // TODO: should be two different values, right?
const auto& to = value;
attr->set(i, util::concat(from, to));
}
return true;
}
return false;
}
bool bindFloat(const std::unique_ptr<FloatBinder>& binder,
gfx::VertexAttributeArray& attrs,
std::string attributeName) {
return binder && bindFloat(*binder, attrs, std::move(attributeName));
}
bool bindFloatPair(const FloatPairBinder& binder, gfx::VertexAttributeArray& attrs, std::string attributeName) {
const auto count = binder.getVertexCount();
// check that vertexVector.elements() == sum(segments.vertexLength)
if (auto& attr = attrs.getOrAdd(std::move(attributeName))) {
for (std::size_t i = 0; i < count; ++i) {
const auto& value = static_cast<const FloatPairVertex*>(binder.getVertexValue(i))->a1;
const auto& from = value; // TODO: should be two different values, right?
const auto& to = value;
attr->set(i, util::concat(from, to));
}
return true;
}
return false;
}
bool bindFloatPair(const std::unique_ptr<FloatPairBinder>& binder,
gfx::VertexAttributeArray& attrs,
std::string attributeName) {
return binder && bindFloatPair(*binder, attrs, std::move(attributeName));
}
bool bindColor(const ColorBinder& binder, gfx::VertexAttributeArray& attrs, std::string attributeName) {
const auto count = binder.getVertexCount();
// check that vertexVector.elements() == sum(segments.vertexLength)
if (auto& attr = attrs.getOrAdd(std::move(attributeName))) {
for (std::size_t i = 0; i < count; ++i) {
const auto& value = static_cast<const FloatPairVertex*>(binder.getVertexValue(i))->a1;
const auto& from = value; // TODO: should be two different values, right?
const auto& to = value;
attr->set(i, util::concat(from, to));
}
return true;
}
return false;
}
bool bindColor(const std::unique_ptr<ColorBinder>& binder,
gfx::VertexAttributeArray& attrs,
std::string attributeName) {
return binder && bindColor(*binder, attrs, std::move(attributeName));
}
bool bindImage(const ImageBinder& binder, gfx::VertexAttributeArray& attrs, std::string attributeName) {
const auto count = binder.getVertexCount();
// check that vertexVector.elements() == sum(segments.vertexLength)
if (auto& attr = attrs.getOrAdd(std::move(attributeName))) {
for (std::size_t i = 0; i < count; ++i) {
const auto& value = static_cast<const UShortQuadVertex*>(binder.getVertexValue(i))->a1;
attr->set(i, util::cast<int32_t>(value));
}
return true;
}
return false;
}
bool bindImage(const std::unique_ptr<ImageBinder>& binder,
gfx::VertexAttributeArray& attrs,
std::string attributeName) {
return binder && bindImage(*binder, attrs, std::move(attributeName));
}
void RenderFillLayer::update(gfx::ShaderRegistry& shaders,
gfx::Context& context,
const TransformState& /*state*/,
@@ -329,276 +426,195 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders,
gfx::VertexAttributeArray patternVertexAttrs;
gfx::VertexAttributeArray patternOutlineVertexAttrs;
const auto finish = [&](gfx::DrawableBuilder& builder, RenderPass pass, const OverscaledTileID& tileID) {
constexpr auto samplerLocation = 0;
const auto layerPrefix = getID() + "/";
const auto renderPass = static_cast<RenderPass>(evaluatedProperties->renderPasses);
const auto finish = [&](gfx::DrawableBuilder& builder, const OverscaledTileID& tileID) {
builder.flush();
for (auto& drawable : builder.clearDrawables()) {
drawable->setTileID(tileID);
tileLayerGroup->addDrawable(pass, tileID, std::move(drawable));
tileLayerGroup->addDrawable(renderPass, tileID, std::move(drawable));
++stats.tileDrawablesAdded;
}
};
const auto commonInit = [](gfx::DrawableBuilder& builder) {
const auto commonInit = [&](gfx::DrawableBuilder& builder) {
builder.setColorAttrMode(gfx::DrawableBuilder::ColorAttrMode::None);
builder.setCullFaceMode(gfx::CullFaceMode::disabled());
builder.setNeedsStencil(true);
};
for (const auto renderPass : {RenderPass::Opaque, RenderPass::Translucent}) {
if (!(mbgl::underlying_type(renderPass) & evaluatedProperties->renderPasses)) {
tileLayerGroup->observeDrawables([&](gfx::UniqueDrawable& drawable) {
// If the render pass has changed or the tile has dropped out of the cover set, remove it.
const auto tileID = drawable->getTileID();
if (drawable->getRenderPass() != renderPass || (tileID && newTileIDs.find(*tileID) == newTileIDs.end())) {
drawable.reset();
++stats.tileDrawablesRemoved;
}
});
for (const RenderTile& tile : *renderTiles) {
const auto& tileID = tile.getOverscaledTileID();
// If we already have drawables for this tile, skip.
if (tileLayerGroup->getDrawableCount(renderPass, tileID) > 0) {
continue;
}
tileLayerGroup->observeDrawables([&](gfx::UniqueDrawable& drawable) {
// Has this tile dropped out of the cover set?
const auto tileID = drawable->getTileID();
if (tileID && newTileIDs.find(*tileID) == newTileIDs.end()) {
// remove it
drawable.reset();
++stats.tileDrawablesRemoved;
const LayerRenderData* renderData = getRenderDataForPass(tile, renderPass);
if (!renderData) {
removeTile(renderPass, tileID);
continue;
}
auto& bucket = static_cast<FillBucket&>(*renderData->bucket);
const auto& evaluated = getEvaluated<FillLayerProperties>(renderData->layerProperties);
const auto& crossfade = getCrossfade<FillLayerProperties>(renderData->layerProperties);
std::vector<std::array<int16_t, 2>> rawVerts;
const auto buildVertices = [&]() {
const std::vector<gfx::VertexVector<gfx::detail::VertexType<gfx::AttributeType<int16_t, 2>>>::Vertex>&
verts = bucket.vertices.vector();
if (rawVerts.size() < verts.size()) {
rawVerts.resize(verts.size());
std::transform(verts.begin(), verts.end(), rawVerts.begin(), [](const auto& x) { return x.a1; });
}
});
};
constexpr auto samplerLocation = 0;
const auto layerPrefix = getID() + "/";
fillVertexAttrs.clear();
outlineVertexAttrs.clear();
for (const RenderTile& tile : *renderTiles) {
const auto& tileID = tile.getOverscaledTileID();
const auto& paintPropertyBinders = bucket.paintPropertyBinders.at(getID());
// If we already have drawables for this tile, skip.
// If a drawable needs to be updated, that's handled in the layer tweaker.
if (tileLayerGroup->getDrawableCount(renderPass, tileID) > 0) {
if (unevaluated.get<FillPattern>().isUndefined()) {
// Fill will occur in opaque or translucent pass based on `opaquePassCutoff`.
// Outline always occurs in translucent pass, defaults to fill color
const auto doOutline = evaluated.get<FillAntialias>();
bindColor(paintPropertyBinders.get<FillColor>(), fillVertexAttrs, "a_color");
bindFloat(paintPropertyBinders.get<FillOpacity>(), fillVertexAttrs, "a_opacity");
if (doOutline) {
bindColor(paintPropertyBinders.get<FillOutlineColor>(), outlineVertexAttrs, "a_outline_color");
bindFloat(paintPropertyBinders.get<FillOpacity>(), outlineVertexAttrs, "a_opacity");
}
if (!fillBuilder && fillShader) {
if (auto builder = context.createDrawableBuilder(layerPrefix + "fill")) {
commonInit(*builder);
builder->setShader(fillShader);
builder->setDepthType((renderPass == RenderPass::Opaque) ? gfx::DepthMaskType::ReadWrite
: gfx::DepthMaskType::ReadOnly);
builder->setSubLayerIndex(0);
builder->setRenderPass(renderPass);
fillBuilder = std::move(builder);
}
}
if (doOutline && !outlineBuilder && outlineShader) {
if (auto builder = context.createDrawableBuilder(layerPrefix + "fill-outline")) {
commonInit(*builder);
builder->setShader(outlineShader);
builder->setLineWidth(2.0f);
builder->setDepthType(gfx::DepthMaskType::ReadOnly);
builder->setSubLayerIndex(unevaluated.get<FillOutlineColor>().isUndefined() ? 2 : 0);
builder->setRenderPass(RenderPass::Translucent);
outlineBuilder = std::move(builder);
}
}
if (fillBuilder) {
buildVertices();
fillBuilder->setVertexAttributes(fillVertexAttrs);
fillBuilder->addVertices(rawVerts, 0, rawVerts.size());
fillBuilder->setSegments(gfx::Triangles(),
bucket.triangles.vector(),
reinterpret_cast<const std::vector<Segment<void>>&>(bucket.triangleSegments));
finish(*fillBuilder, tileID);
}
if (outlineBuilder) {
buildVertices();
outlineBuilder->setVertexAttributes(outlineVertexAttrs);
outlineBuilder->addVertices(rawVerts, 0, rawVerts.size());
outlineBuilder->setSegments(gfx::Lines(2),
bucket.lines.vector(),
reinterpret_cast<const std::vector<Segment<void>>&>(bucket.lineSegments));
finish(*outlineBuilder, tileID);
}
} else { // FillPattern is defined
if ((renderPass & RenderPass::Translucent) == 0) {
continue;
}
const LayerRenderData* renderData = getRenderDataForPass(tile, renderPass);
if (!renderData) {
removeTile(renderPass, tileID);
continue;
// Outline does not default to fill in the pattern case
const auto doOutline = evaluated.get<FillAntialias>() && unevaluated.get<FillOutlineColor>().isUndefined();
const auto& fillPatternValue = evaluated.get<FillPattern>().constantOr(Faded<expression::Image>{"", ""});
const auto patternPosA = tile.getPattern(fillPatternValue.from.id());
const auto patternPosB = tile.getPattern(fillPatternValue.to.id());
paintPropertyBinders.setPatternParameters(patternPosA, patternPosB, crossfade);
// if (samplerLocation < 0) curShader->getSamplerLocation("u_image");
bindFloat(paintPropertyBinders.get<FillOpacity>(), fillVertexAttrs, "a_opacity");
bindImage(paintPropertyBinders.get<FillPattern>(), fillVertexAttrs, "a_pattern_from");
bindImage(paintPropertyBinders.get<FillPattern>(), fillVertexAttrs, "a_pattern_to");
if (doOutline) {
bindFloat(paintPropertyBinders.get<FillOpacity>(), outlineVertexAttrs, "a_opacity");
bindImage(paintPropertyBinders.get<FillPattern>(), outlineVertexAttrs, "a_pattern_from");
bindImage(paintPropertyBinders.get<FillPattern>(), outlineVertexAttrs, "a_pattern_to");
}
auto& bucket = static_cast<FillBucket&>(*renderData->bucket);
const auto& evaluated = getEvaluated<FillLayerProperties>(renderData->layerProperties);
const auto& crossfade = getCrossfade<FillLayerProperties>(renderData->layerProperties);
std::vector<std::array<int16_t, 2>> rawVerts;
const auto buildVertices = [&]() {
const std::vector<gfx::VertexVector<gfx::detail::VertexType<gfx::AttributeType<int16_t, 2>>>::Vertex>&
verts = bucket.vertices.vector();
if (rawVerts.size() < verts.size()) {
rawVerts.resize(verts.size());
std::transform(verts.begin(), verts.end(), rawVerts.begin(), [](const auto& x) { return x.a1; });
}
};
fillVertexAttrs.clear();
outlineVertexAttrs.clear();
const auto& paintPropertyBinders = bucket.paintPropertyBinders.at(getID());
if (unevaluated.get<FillPattern>().isUndefined()) {
const auto evalColor = evaluated.get<FillColor>().constantOr(Color());
const auto fillOpacity = evaluated.get<FillOpacity>().constantOr(0);
const auto fillColor = evalColor * (fillOpacity > 0.0f ? fillOpacity : 1.0f);
const auto fillRenderPass = (fillColor.a >= 1.0f
/* && parameters.currentLayer >= parameters.opaquePassCutoff*/)
? RenderPass::Opaque
: RenderPass::Translucent;
const auto doFill = fillRenderPass == renderPass;
const auto doOutline = evaluated.get<FillAntialias>() && renderPass == RenderPass::Translucent;
if (!doFill && !doOutline) {
removeTile(renderPass, tileID);
continue;
}
if (doFill) {
if (auto& binder = paintPropertyBinders.get<FillColor>()) {
const auto count = binder->getVertexCount();
// check that vertexVector.elements() == sum(segments.vertexLength)
if (auto& attr = fillVertexAttrs.getOrAdd("a_color")) {
for (std::size_t i = 0; i < count; ++i) {
const auto& packed =
static_cast<const gfx::detail::VertexType<gfx::AttributeType<float, 2>>*>(
binder->getVertexValue(i))
->a1;
attr->set<gfx::VertexAttribute::float4>(i,
{packed[0], packed[1], packed[0], packed[1]});
}
}
if (!patternBuilder && patternShader) {
if (auto builder = context.createDrawableBuilder(layerPrefix + "fill-pattern")) {
commonInit(*builder);
builder->setShader(patternShader);
builder->setDepthType(gfx::DepthMaskType::ReadWrite);
builder->setSubLayerIndex(1);
builder->setRenderPass(RenderPass::Translucent);
if (const auto& atlases = tile.getAtlasTextures()) {
builder->setTextureSource(samplerLocation, [=]() { return atlases ? atlases->icon : nullptr; });
}
patternBuilder = std::move(builder);
}
if (doOutline) {
if (auto& binder = paintPropertyBinders.get<FillOutlineColor>()) {
const auto count = binder->getVertexCount();
if (auto& attr = outlineVertexAttrs.getOrAdd("a_outline_color")) {
for (std::size_t i = 0; i < count; ++i) {
const auto& packed =
static_cast<const gfx::detail::VertexType<gfx::AttributeType<float, 2>>*>(
binder->getVertexValue(i))
->a1;
attr->set<gfx::VertexAttribute::float4>(i,
{packed[0], packed[1], packed[0], packed[1]});
}
}
}
}
if (auto& binder = paintPropertyBinders.get<FillOpacity>()) {
const auto count = binder->getVertexCount();
for (auto& attrs :
{std::reference_wrapper(fillVertexAttrs), std::reference_wrapper(outlineVertexAttrs)}) {
if (auto& attr = attrs.get().getOrAdd("a_opacity")) {
for (std::size_t i = 0; i < count; ++i) {
const auto& opacity =
static_cast<const gfx::detail::VertexType<gfx::AttributeType<float, 1>>*>(
binder->getVertexValue(i))
->a1;
attr->set<gfx::VertexAttribute::float2>(i, {opacity[0], opacity[0]});
}
}
}
if (doOutline && !outlinePatternBuilder && outlinePatternShader) {
if (auto builder = context.createDrawableBuilder(layerPrefix + "fill-outline-pattern")) {
commonInit(*builder);
builder->setShader(outlinePatternShader);
builder->setLineWidth(2.0f);
builder->setDepthType(gfx::DepthMaskType::ReadOnly);
builder->setSubLayerIndex(2);
builder->setRenderPass(RenderPass::Translucent);
if (const auto& atlases = tile.getAtlasTextures()) {
builder->setTextureSource(samplerLocation, [=]() { return atlases ? atlases->icon : nullptr; });
}
outlinePatternBuilder = std::move(builder);
}
}
if (doFill && !fillBuilder && fillShader) {
if (auto builder = context.createDrawableBuilder(layerPrefix + "fill")) {
commonInit(*builder);
builder->setShader(fillShader);
builder->setDepthType((renderPass == RenderPass::Opaque) ? gfx::DepthMaskType::ReadWrite
: gfx::DepthMaskType::ReadOnly);
builder->setSubLayerIndex(0);
fillBuilder = std::move(builder);
}
}
if (doOutline && !outlineBuilder && outlineShader) {
if (auto builder = context.createDrawableBuilder(layerPrefix + "fill-outline")) {
commonInit(*builder);
builder->setShader(outlineShader);
builder->setLineWidth(2.0f);
builder->setDepthType(gfx::DepthMaskType::ReadOnly);
builder->setSubLayerIndex(unevaluated.get<FillOutlineColor>().isUndefined() ? 2 : 0);
outlineBuilder = std::move(builder);
}
}
if (fillBuilder) {
buildVertices();
fillBuilder->setRenderPass(renderPass);
fillBuilder->setVertexAttributes(fillVertexAttrs);
fillBuilder->addVertices(rawVerts, 0, rawVerts.size());
fillBuilder->setSegments(
gfx::Triangles(),
bucket.triangles.vector(),
reinterpret_cast<const std::vector<Segment<void>>&>(bucket.triangleSegments));
finish(*fillBuilder, renderPass, tileID);
}
if (outlineBuilder) {
buildVertices();
outlineBuilder->setRenderPass(renderPass);
outlineBuilder->setVertexAttributes(outlineVertexAttrs);
outlineBuilder->addVertices(rawVerts, 0, rawVerts.size());
outlineBuilder->setSegments(
gfx::Lines(2),
bucket.lines.vector(),
reinterpret_cast<const std::vector<Segment<void>>&>(bucket.lineSegments));
finish(*outlineBuilder, renderPass, tileID);
}
} else { // FillPattern is defined
if (renderPass != RenderPass::Translucent) {
continue;
}
const auto doOutline = evaluated.get<FillAntialias>() &&
unevaluated.get<FillOutlineColor>().isUndefined();
const auto& fillPatternValue = evaluated.get<FillPattern>().constantOr(
Faded<expression::Image>{"", ""});
const auto patternPosA = tile.getPattern(fillPatternValue.from.id());
const auto patternPosB = tile.getPattern(fillPatternValue.to.id());
paintPropertyBinders.setPatternParameters(patternPosA, patternPosB, crossfade);
// if (samplerLocation < 0) curShader->getSamplerLocation("u_image");
if (auto& binder = paintPropertyBinders.get<FillOpacity>()) {
const auto count = binder->getVertexCount();
for (auto& attrs : {std::reference_wrapper(patternVertexAttrs),
std::reference_wrapper(patternOutlineVertexAttrs)}) {
if (auto& attr = attrs.get().getOrAdd("a_opacity")) {
for (std::size_t i = 0; i < count; ++i) {
const auto& opacity =
static_cast<const gfx::detail::VertexType<gfx::AttributeType<float, 1>>*>(
binder->getVertexValue(i))
->a1;
attr->set<gfx::VertexAttribute::float2>(i, {opacity[0], opacity[0]});
}
}
}
}
// if (auto& binder = paintPropertyBinders.get<FillOpacity>()) {
// const auto count = binder->getVertexCount();
// for (auto& attrs :
// {std::reference_wrapper(patternVertexAttrs),
// std::reference_wrapper(patternOutlineVertexAttrs)}) {
// if (auto& attr = attrs.get().getOrAdd("a_pattern_from")) {
// for (std::size_t i = 0; i < count; ++i) {
// const auto& opacity =
// static_cast<const
// gfx::detail::VertexType<gfx::AttributeType<float, 4>>*>(
// binder->getVertexValue(i))
// ->a1;
// attr->set<gfx::VertexAttribute::float2>(i, {});
// }
// }
// }
// }
if (!patternBuilder && patternShader) {
if (auto builder = context.createDrawableBuilder(layerPrefix + "fill-pattern")) {
commonInit(*builder);
builder->setShader(fillShader);
builder->setDepthType(gfx::DepthMaskType::ReadWrite);
builder->setSubLayerIndex(1);
if (auto& tex = tile.getIconAtlasTexture()) {
builder->setTexture(tex, samplerLocation);
}
patternBuilder = std::move(builder);
}
}
if (doOutline && !outlinePatternBuilder && outlinePatternShader) {
if (auto builder = context.createDrawableBuilder(layerPrefix + "fill-outline-pattern")) {
commonInit(*builder);
builder->setShader(outlineShader);
builder->setLineWidth(2.0f);
builder->setDepthType(gfx::DepthMaskType::ReadOnly);
builder->setSubLayerIndex(2);
if (auto& tex = tile.getIconAtlasTexture()) {
builder->setTexture(tex, samplerLocation);
}
outlinePatternBuilder = std::move(builder);
}
}
if (patternBuilder) {
buildVertices();
patternBuilder->setRenderPass(renderPass);
patternBuilder->setVertexAttributes(fillVertexAttrs);
patternBuilder->addVertices(rawVerts, 0, rawVerts.size());
patternBuilder->setSegments(
gfx::Triangles(),
bucket.triangles.vector(),
reinterpret_cast<const std::vector<Segment<void>>&>(bucket.triangleSegments));
finish(*patternBuilder, renderPass, tileID);
}
if (outlinePatternBuilder) {
buildVertices();
outlinePatternBuilder->setRenderPass(renderPass);
outlinePatternBuilder->setVertexAttributes(outlineVertexAttrs);
outlinePatternBuilder->addVertices(rawVerts, 0, rawVerts.size());
outlinePatternBuilder->setSegments(
gfx::Lines(2),
bucket.lines.vector(),
reinterpret_cast<const std::vector<Segment<void>>&>(bucket.lineSegments));
finish(*outlinePatternBuilder, renderPass, tileID);
}
if (patternBuilder) {
buildVertices();
patternBuilder->setRenderPass(renderPass);
patternBuilder->setVertexAttributes(fillVertexAttrs);
patternBuilder->addVertices(rawVerts, 0, rawVerts.size());
patternBuilder->setSegments(
gfx::Triangles(),
bucket.triangles.vector(),
reinterpret_cast<const std::vector<Segment<void>>&>(bucket.triangleSegments));
finish(*patternBuilder, tileID);
}
if (outlinePatternBuilder) {
buildVertices();
outlinePatternBuilder->setRenderPass(renderPass);
outlinePatternBuilder->setVertexAttributes(outlineVertexAttrs);
outlinePatternBuilder->addVertices(rawVerts, 0, rawVerts.size());
outlinePatternBuilder->setSegments(
gfx::Lines(2),
bucket.lines.vector(),
reinterpret_cast<const std::vector<Segment<void>>&>(bucket.lineSegments));
finish(*outlinePatternBuilder, tileID);
}
}
}