Triangulated fill outlines (Metal) (#1880)

This commit is contained in:
Stefan Karschti
2023-11-24 14:19:41 +02:00
committed by GitHub
parent 09e16d78d8
commit 893c97f20e
21 changed files with 821 additions and 68 deletions

View File

@@ -31,6 +31,7 @@
#include <mbgl/shaders/fill_layer_ubo.hpp>
#include <mbgl/shaders/shader_program_base.hpp>
#include <mbgl/util/string_indexer.hpp>
#include <mbgl/shaders/line_layer_ubo.hpp>
#endif
namespace mbgl {
@@ -42,6 +43,9 @@ namespace {
#if MLN_DRAWABLE_RENDERER
constexpr auto FillShaderName = "FillShader";
#if MLN_TRIANGULATE_FILL_OUTLINES
constexpr auto FillOutlineTriangulatedShaderName = "LineBasicShader";
#endif
constexpr auto FillOutlineShaderName = "FillOutlineShader";
constexpr auto FillPatternShaderName = "FillPatternShader";
constexpr auto FillOutlinePatternShaderName = "FillOutlinePatternShader";
@@ -109,6 +113,49 @@ bool RenderFillLayer::hasCrossfade() const {
return getCrossfade<FillLayerProperties>(evaluatedProperties).t != 1;
}
#if MLN_TRIANGULATE_FILL_OUTLINES
void RenderFillLayer::markLayerRenderable(bool willRender, UniqueChangeRequestVec& changes) {
RenderLayer::markLayerRenderable(willRender, changes);
if (outlineLayerGroup) {
activateLayerGroup(outlineLayerGroup, willRender, changes);
}
}
void RenderFillLayer::layerRemoved(UniqueChangeRequestVec& changes) {
RenderLayer::layerRemoved(changes);
if (outlineLayerGroup) {
activateLayerGroup(outlineLayerGroup, false, changes);
}
}
void RenderFillLayer::layerIndexChanged(int32_t newLayerIndex, UniqueChangeRequestVec& changes) {
RenderLayer::layerIndexChanged(newLayerIndex, changes);
changeLayerIndex(outlineLayerGroup, newLayerIndex, changes);
}
std::size_t RenderFillLayer::removeAllDrawables() {
auto count = RenderLayer::removeAllDrawables();
if (outlineLayerGroup) {
auto count2 = outlineLayerGroup->clearDrawables();
stats.drawablesRemoved += count2;
count += count2;
}
return count;
}
std::size_t RenderFillLayer::removeTile(RenderPass renderPass, const OverscaledTileID& tileID) {
RenderLayer::removeTile(renderPass, tileID);
if (const auto tileGroup = static_cast<TileLayerGroup*>(outlineLayerGroup.get())) {
const auto n = tileGroup->removeDrawables(renderPass, tileID).size();
stats.drawablesRemoved += n;
return n;
}
return 0;
}
#endif // MLN_TRIANGULATE_FILL_OUTLINES
#if MLN_LEGACY_RENDERER
void RenderFillLayer::render(PaintParameters& parameters) {
assert(renderTiles);
@@ -188,7 +235,7 @@ void RenderFillLayer::render(PaintParameters& parameters) {
parameters.depthModeForSublayer(unevaluated.get<FillOutlineColor>().isUndefined() ? 2 : 0,
gfx::DepthMaskType::ReadOnly),
*bucket.lineIndexBuffer,
bucket.lineSegments,
bucket.basicLineSegments,
FillOutlineProgram::TextureBindings{});
}
}
@@ -269,7 +316,7 @@ void RenderFillLayer::render(PaintParameters& parameters) {
gfx::Lines{2.0f},
parameters.depthModeForSublayer(2, gfx::DepthMaskType::ReadOnly),
*bucket.lineIndexBuffer,
bucket.lineSegments,
bucket.basicLineSegments,
FillOutlinePatternProgram::TextureBindings{
tile.getIconAtlasTextureBinding(gfx::TextureFilterType::Linear),
});
@@ -299,6 +346,62 @@ bool RenderFillLayer::queryIntersectsFeature(const GeometryCoordinates& queryGeo
#if MLN_DRAWABLE_RENDERER
#if MLN_TRIANGULATE_FILL_OUTLINES
class OutlineDrawableTweaker : public gfx::DrawableTweaker {
public:
OutlineDrawableTweaker(Color color_, float opacity_)
: color(color_),
opacity(opacity_) {}
~OutlineDrawableTweaker() override = default;
void init(gfx::Drawable&) override{};
void execute(gfx::Drawable& drawable, const PaintParameters& parameters) override {
if (!drawable.getTileID().has_value()) {
return;
}
const UnwrappedTileID tileID = drawable.getTileID()->toUnwrapped();
const auto zoom = parameters.state.getZoom();
auto& uniforms = drawable.mutableUniformBuffers();
static const StringIdentity idLineUBOName = stringIndexer().get("LineBasicUBO");
{
const auto matrix = LayerTweaker::getTileMatrix(
tileID, parameters, {{0, 0}}, style::TranslateAnchorType::Viewport, false, false, false);
const shaders::LineBasicUBO lineUBO{
/*matrix = */ util::cast<float>(matrix),
/*units_to_pixels = */ {1.0f / parameters.pixelsToGLUnits[0], 1.0f / parameters.pixelsToGLUnits[1]},
/*ratio = */ 1.0f / tileID.pixelsToTileUnits(1.0f, zoom),
/*device_pixel_ratio = */ parameters.pixelRatio};
parameters.context.emplaceOrUpdateUniformBuffer(lineUniformBuffer, &lineUBO);
}
uniforms.addOrReplace(idLineUBOName, lineUniformBuffer);
static const StringIdentity idLinePropertiesUBOName = stringIndexer().get("LineBasicPropertiesUBO");
if (!linePropertiesUniformBuffer) {
const shaders::LineBasicPropertiesUBO linePropertiesUBO{/*color =*/color,
/*opacity =*/opacity,
/*width =*/1.f,
0,
0};
parameters.context.emplaceOrUpdateUniformBuffer(linePropertiesUniformBuffer, &linePropertiesUBO);
}
if (!uniforms.get(idLinePropertiesUBOName)) {
uniforms.addOrReplace(idLinePropertiesUBOName, linePropertiesUniformBuffer);
}
};
private:
Color color;
float opacity;
gfx::UniformBufferPtr lineUniformBuffer;
gfx::UniformBufferPtr linePropertiesUniformBuffer;
};
#endif // MLN_TRIANGULATE_FILL_OUTLINES
void RenderFillLayer::update(gfx::ShaderRegistry& shaders,
gfx::Context& context,
const TransformState& state,
@@ -312,7 +415,7 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders,
return;
}
// Set up a layer group
// Set up a layer group for fill
if (!layerGroup) {
if (auto layerGroup_ = context.createTileLayerGroup(layerIndex, /*initialCapacity=*/64, getID())) {
setLayerGroup(std::move(layerGroup_), changes);
@@ -320,7 +423,19 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders,
return;
}
}
auto* tileLayerGroup = static_cast<TileLayerGroup*>(layerGroup.get());
auto* fillTileLayerGroup = static_cast<TileLayerGroup*>(layerGroup.get());
#if MLN_TRIANGULATE_FILL_OUTLINES
// Set up a layer group for outlines
if (!outlineLayerGroup) {
outlineLayerGroup = context.createTileLayerGroup(layerIndex, /*initialCapacity=*/64, getID());
activateLayerGroup(outlineLayerGroup, isRenderable, changes);
}
auto* outlineTileLayerGroup = static_cast<TileLayerGroup*>(outlineLayerGroup.get());
if (!outlineTriangulatedShaderGroup) {
outlineTriangulatedShaderGroup = shaders.getShaderGroup(std::string(FillOutlineTriangulatedShaderName));
}
#endif
if (!layerTweaker) {
layerTweaker = std::make_shared<FillLayerTweaker>(getID(), evaluatedProperties);
@@ -358,13 +473,23 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders,
builder.setEnableStencil(true);
};
stats.drawablesRemoved += tileLayerGroup->removeDrawablesIf([&](gfx::Drawable& drawable) {
#if MLN_TRIANGULATE_FILL_OUTLINES
for (auto layer : {fillTileLayerGroup, outlineTileLayerGroup}) {
stats.drawablesRemoved += layer->removeDrawablesIf([&](gfx::Drawable& drawable) {
// If the render pass has changed or the tile has dropped out of the cover set, remove it.
const auto& tileID = drawable.getTileID();
return tileID && !hasRenderTile(*tileID);
});
}
#else
stats.drawablesRemoved += fillTileLayerGroup->removeDrawablesIf([&](gfx::Drawable& drawable) {
// If the render pass has changed or the tile has dropped out of the cover set, remove it.
const auto& tileID = drawable.getTileID();
return tileID && !hasRenderTile(*tileID);
});
#endif
tileLayerGroup->setStencilTiles(renderTiles);
fillTileLayerGroup->setStencilTiles(renderTiles);
std::unordered_set<StringIdentity> propertiesAsUniforms;
for (const RenderTile& tile : *renderTiles) {
@@ -554,16 +679,76 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders,
continue;
}
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>();
// Outline always occurs in translucent pass, defaults to fill color
// Outline does not default to fill in the pattern case
const auto doOutline = evaluated.get<FillAntialias>() && (unevaluated.get<FillPattern>().isUndefined() ||
unevaluated.get<FillOutlineColor>().isUndefined());
#if MLN_TRIANGULATE_FILL_OUTLINES
const bool dataDrivenOutline = !evaluated.get<FillOutlineColor>().isConstant() ||
!evaluated.get<FillOpacity>().isConstant();
#endif
if (unevaluated.get<FillPattern>().isUndefined()) {
// Simple fill
if (!fillShaderGroup || (doOutline && !outlineShaderGroup)) {
continue;
}
const auto fillShader = std::static_pointer_cast<gfx::ShaderProgramBase>(
fillShaderGroup->getOrCreateShader(context, propertiesAsUniforms));
#if MLN_TRIANGULATE_FILL_OUTLINES
const auto outlineTriangulatedShader = doOutline && !dataDrivenOutline ? [&]() -> auto {
static const std::unordered_set<StringIdentity> outlinePropertiesAsUniforms{
stringIndexer().get("a_color"),
stringIndexer().get("a_opacity"),
stringIndexer().get("a_width"),
};
return std::static_pointer_cast<gfx::ShaderProgramBase>(
outlineTriangulatedShaderGroup->getOrCreateShader(context, outlinePropertiesAsUniforms));
}()
: nullptr;
auto createOutline = [&](auto& builder, Color color, float opacity) {
if (doOutline && builder && bucket.sharedLineIndexes->elements()) {
static const StringIdentity idVertexAttribName = stringIndexer().get("a_pos_normal");
static const StringIdentity idDataAttribName = stringIndexer().get("a_data");
builder->setVertexAttrNameId(idVertexAttribName);
builder->setShader(outlineTriangulatedShader);
builder->setRawVertices({}, bucket.lineVertices.elements(), gfx::AttributeDataType::Short2);
gfx::VertexAttributeArray attrs;
if (const auto& attr = attrs.add(idVertexAttribName)) {
attr->setSharedRawData(bucket.sharedLineVertices,
offsetof(LineLayoutVertex, a1),
/*vertexOffset=*/0,
sizeof(LineLayoutVertex),
gfx::AttributeDataType::Short2);
}
if (const auto& attr = attrs.add(idDataAttribName)) {
attr->setSharedRawData(bucket.sharedLineVertices,
offsetof(LineLayoutVertex, a2),
/*vertexOffset=*/0,
sizeof(LineLayoutVertex),
gfx::AttributeDataType::UByte4);
}
builder->setVertexAttributes(std::move(attrs));
builder->setSegments(gfx::Triangles(),
bucket.sharedLineIndexes,
bucket.lineSegments.data(),
bucket.lineSegments.size());
auto tweaker = std::make_shared<OutlineDrawableTweaker>(color, opacity);
// finish
builder->flush();
for (auto& drawable : builder->clearDrawables()) {
drawable->setTileID(tileID);
drawable->addTweaker(tweaker);
outlineTileLayerGroup->addDrawable(renderPass, tileID, std::move(drawable));
++stats.drawablesAdded;
}
}
};
#endif
const auto outlineShader = doOutline
? std::static_pointer_cast<gfx::ShaderProgramBase>(
outlineShaderGroup->getOrCreateShader(context, propertiesAsUniforms))
@@ -586,10 +771,10 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders,
if (doOutline && !outlineBuilder && outlineShader) {
if (auto builder = context.createDrawableBuilder(layerPrefix + "fill-outline")) {
commonInit(*builder);
builder->setLineWidth(2.0f);
builder->setDepthType(gfx::DepthMaskType::ReadOnly);
builder->setColorMode(gfx::ColorMode::alphaBlended());
builder->setLineWidth(2.0f);
builder->setSubLayerIndex(unevaluated.get<FillOutlineColor>().isUndefined() ? 2 : 0);
builder->setColorMode(gfx::ColorMode::alphaBlended());
builder->setRenderPass(RenderPass::Translucent);
outlineBuilder = std::move(builder);
}
@@ -599,6 +784,7 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders,
const StringIdentity interpolateUBONameId,
const auto& interpolateUBO,
FillVariant type) {
builder.setVertexAttrNameId(idPosAttribName);
builder.flush();
for (auto& drawable : builder.clearDrawables()) {
@@ -608,19 +794,28 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders,
auto& uniforms = drawable->mutableUniformBuffers();
uniforms.createOrUpdate(interpolateUBONameId, &interpolateUBO, context);
tileLayerGroup->addDrawable(renderPass, tileID, std::move(drawable));
fillTileLayerGroup->addDrawable(renderPass, tileID, std::move(drawable));
++stats.drawablesAdded;
}
};
if (fillBuilder && bucket.sharedTriangles->elements()) {
fillBuilder->setShader(fillShader);
if (outlineBuilder) {
#if MLN_TRIANGULATE_FILL_OUTLINES
if (doOutline && dataDrivenOutline && outlineBuilder) {
fillBuilder->setVertexAttributes(vertexAttrs);
outlineBuilder->setVertexAttributes(std::move(vertexAttrs));
} else {
fillBuilder->setVertexAttributes(std::move(vertexAttrs));
}
#else
if (doOutline && outlineBuilder) {
fillBuilder->setVertexAttributes(vertexAttrs);
outlineBuilder->setVertexAttributes(std::move(vertexAttrs));
} else {
fillBuilder->setVertexAttributes(std::move(vertexAttrs));
}
#endif
fillBuilder->setRawVertices({}, vertexCount, gfx::AttributeDataType::Short2);
fillBuilder->setSegments(gfx::Triangles(),
bucket.sharedTriangles,
@@ -631,24 +826,49 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders,
getFillInterpolateUBO(),
FillVariant::Fill);
}
if (outlineBuilder && bucket.sharedLines->elements()) {
#if MLN_TRIANGULATE_FILL_OUTLINES
if (doOutline && outlineBuilder) {
if (!dataDrivenOutline) {
outlineBuilder->setSubLayerIndex(unevaluated.get<FillOutlineColor>().isUndefined() ? 2 : 0);
createOutline(outlineBuilder,
evaluated.get<FillOutlineColor>().constantOr(FillOutlineColor::defaultValue()),
evaluated.get<FillOpacity>().constantOr(FillOpacity::defaultValue()));
} else {
if (bucket.sharedBasicLineIndexes->elements()) {
outlineBuilder->setShader(outlineShader);
outlineBuilder->setRawVertices({}, vertexCount, gfx::AttributeDataType::Short2);
outlineBuilder->setSegments(gfx::Lines(2),
bucket.sharedBasicLineIndexes,
bucket.basicLineSegments.data(),
bucket.basicLineSegments.size());
finish(*outlineBuilder,
FillLayerTweaker::idFillOutlineInterpolateUBOName,
getFillOutlineInterpolateUBO(),
FillVariant::FillOutline);
}
}
}
#else
if (doOutline && outlineBuilder && bucket.sharedBasicLineIndexes->elements()) {
outlineBuilder->setShader(outlineShader);
outlineBuilder->setRawVertices({}, vertexCount, gfx::AttributeDataType::Short2);
outlineBuilder->setSegments(
gfx::Lines(2), bucket.sharedLines, bucket.lineSegments.data(), bucket.lineSegments.size());
outlineBuilder->setSegments(gfx::Lines(2),
bucket.sharedBasicLineIndexes,
bucket.basicLineSegments.data(),
bucket.basicLineSegments.size());
finish(*outlineBuilder,
FillLayerTweaker::idFillOutlineInterpolateUBOName,
getFillOutlineInterpolateUBO(),
FillVariant::FillOutline);
}
} else { // FillPattern is defined
#endif
} else {
// Fill with pattern
if ((renderPass & RenderPass::Translucent) == 0) {
continue;
}
// Outline does not default to fill in the pattern case
const auto doOutline = evaluated.get<FillAntialias>() && unevaluated.get<FillOutlineColor>().isUndefined();
if (!patternShaderGroup || (doOutline && !outlinePatternShaderGroup)) {
continue;
}
@@ -688,7 +908,7 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders,
patternBuilder->clearTweakers();
patternBuilder->addTweaker(getAtlasTweaker());
}
if (outlinePatternBuilder) {
if (doOutline && outlinePatternBuilder) {
outlinePatternBuilder->clearTweakers();
outlinePatternBuilder->addTweaker(getAtlasTweaker());
}
@@ -709,7 +929,7 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders,
auto& uniforms = drawable->mutableUniformBuffers();
uniforms.createOrUpdate(interpolateNameId, &interpolateUBO, context);
uniforms.createOrUpdate(tileUBONameId, &tileUBO, context);
tileLayerGroup->addDrawable(renderPass, tileID, std::move(drawable));
fillTileLayerGroup->addDrawable(renderPass, tileID, std::move(drawable));
++stats.drawablesAdded;
}
};
@@ -717,7 +937,7 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders,
if (patternBuilder && bucket.sharedTriangles->elements()) {
patternBuilder->setShader(fillShader);
patternBuilder->setRenderPass(renderPass);
if (outlinePatternBuilder) {
if (doOutline && outlinePatternBuilder) {
patternBuilder->setVertexAttributes(vertexAttrs);
outlinePatternBuilder->setVertexAttributes(std::move(vertexAttrs));
} else {
@@ -736,12 +956,15 @@ void RenderFillLayer::update(gfx::ShaderRegistry& shaders,
getFillPatternTilePropsUBO(),
FillVariant::FillPattern);
}
if (outlinePatternBuilder && bucket.sharedLines->elements()) {
if (doOutline && outlinePatternBuilder && bucket.sharedBasicLineIndexes->elements()) {
outlinePatternBuilder->setShader(outlineShader);
outlinePatternBuilder->setRenderPass(renderPass);
outlinePatternBuilder->setRawVertices({}, vertexCount, gfx::AttributeDataType::Short2);
outlinePatternBuilder->setSegments(
gfx::Lines(2), bucket.sharedLines, bucket.lineSegments.data(), bucket.lineSegments.size());
outlinePatternBuilder->setSegments(gfx::Lines(2),
bucket.sharedBasicLineIndexes,
bucket.basicLineSegments.data(),
bucket.basicLineSegments.size());
finish(*outlinePatternBuilder,
idFillOutlinePatternInterpolateUBOName,