Custom Drawable Layer: Symbol Icon (#1908)

This commit is contained in:
Stefan Karschti
2024-02-16 17:39:47 +02:00
committed by GitHub
parent 612b256821
commit d6db72e8ba
32 changed files with 789 additions and 30 deletions

View File

@@ -1,167 +0,0 @@
#pragma once
#include <mbgl/gfx/backend.hpp>
#include <mbgl/gfx/command_encoder.hpp>
#include <mbgl/gfx/draw_scope.hpp>
#include <mbgl/gfx/program.hpp>
#include <mbgl/gfx/renderbuffer.hpp>
#include <mbgl/gfx/rendering_stats.hpp>
#include <mbgl/gfx/texture.hpp>
#include <mbgl/gfx/types.hpp>
#if MLN_DRAWABLE_RENDERER
#include <mbgl/gfx/uniform_buffer.hpp>
#endif
#include <memory>
#include <string>
namespace mbgl {
class PaintParameters;
class ProgramParameters;
#if MLN_DRAWABLE_RENDERER
class TileLayerGroup;
class LayerGroup;
class RenderTarget;
using TileLayerGroupPtr = std::shared_ptr<TileLayerGroup>;
using LayerGroupPtr = std::shared_ptr<LayerGroup>;
using RenderTargetPtr = std::shared_ptr<RenderTarget>;
#endif
namespace gfx {
class OffscreenTexture;
class ShaderRegistry;
#if MLN_DRAWABLE_RENDERER
class Drawable;
class DrawableBuilder;
class ShaderProgramBase;
class Texture2D;
class VertexAttributeArray;
using DrawablePtr = std::shared_ptr<Drawable>;
using ShaderProgramBasePtr = std::shared_ptr<ShaderProgramBase>;
using Texture2DPtr = std::shared_ptr<Texture2D>;
using UniformBufferPtr = std::shared_ptr<UniformBuffer>;
using UniqueDrawableBuilder = std::unique_ptr<DrawableBuilder>;
using VertexAttributeArrayPtr = std::shared_ptr<VertexAttributeArray>;
#endif
class Context {
protected:
Context(uint32_t maximumVertexBindingCount_)
: maximumVertexBindingCount(maximumVertexBindingCount_) {}
public:
static constexpr const uint32_t minimumRequiredVertexBindingCount = 8;
const uint32_t maximumVertexBindingCount;
Context(Context&&) = delete;
Context(const Context&) = delete;
Context& operator=(Context&& other) = delete;
Context& operator=(const Context& other) = delete;
virtual ~Context() = default;
virtual void beginFrame() = 0;
virtual void endFrame() = 0;
/// Called at the end of a frame.
virtual void performCleanup() = 0;
/// Called when the app receives a memory warning and before it goes to the background.
virtual void reduceMemoryUsage() = 0;
virtual std::unique_ptr<OffscreenTexture> createOffscreenTexture(Size, TextureChannelDataType) = 0;
/// Creates an empty texture with the specified dimensions.
Texture createTexture(const Size size,
TexturePixelType format = TexturePixelType::RGBA,
TextureChannelDataType type = TextureChannelDataType::UnsignedByte) {
return {size, createTextureResource(size, format, type)};
}
template <RenderbufferPixelType pixelType>
Renderbuffer<pixelType> createRenderbuffer(const Size size) {
return {size, createRenderbufferResource(pixelType, size)};
}
DrawScope createDrawScope() { return DrawScope{createDrawScopeResource()}; }
virtual std::unique_ptr<CommandEncoder> createCommandEncoder() = 0;
gfx::RenderingStats& renderingStats() { return stats; }
const gfx::RenderingStats& renderingStats() const { return stats; }
#if !defined(NDEBUG)
virtual void visualizeStencilBuffer() = 0;
virtual void visualizeDepthBuffer(float depthRangeSize) = 0;
#endif
virtual void clearStencilBuffer(int32_t) = 0;
/// Sets dirty state
virtual void setDirtyState() = 0;
#if MLN_DRAWABLE_RENDERER
/// Create a new vertex attribute array
virtual gfx::VertexAttributeArrayPtr createVertexAttributeArray() const = 0;
/// Create a new drawable builder
virtual UniqueDrawableBuilder createDrawableBuilder(std::string name) = 0;
/// Create a new uniform buffer
/// @param data The data to copy, may be `nullptr`
/// @param size The size of the buffer
/// @param persistent Performance hint, optimize for few or many uses
virtual UniformBufferPtr createUniformBuffer(const void* data, std::size_t size, bool persistent = false) = 0;
/// Get the generic shader with the specified name
virtual gfx::ShaderProgramBasePtr getGenericShader(gfx::ShaderRegistry&, const std::string& name) = 0;
/// Create a tile layer group implementation
virtual TileLayerGroupPtr createTileLayerGroup(int32_t layerIndex,
std::size_t initialCapacity,
std::string name) = 0;
/// Create a layer group implementation
virtual LayerGroupPtr createLayerGroup(int32_t layerIndex, std::size_t initialCapacity, std::string name) = 0;
/// Create a texture
virtual Texture2DPtr createTexture2D() = 0;
/// Create a render target
virtual RenderTargetPtr createRenderTarget(const Size size, const TextureChannelDataType type) = 0;
/// Resets the context state to defaults
virtual void resetState(gfx::DepthMode depthMode, gfx::ColorMode colorMode) = 0;
/// Update the uniform buffer with the provided data if it already exists, otherwise create it.
/// @return True if the buffer was created, false if it was updated
virtual bool emplaceOrUpdateUniformBuffer(gfx::UniformBufferPtr&,
const void* data,
std::size_t size,
bool persistent = false) = 0;
/// `emplaceOrUpdateUniformBuffer` with type inference
template <typename T>
std::enable_if_t<!std::is_pointer_v<T>, bool> emplaceOrUpdateUniformBuffer(gfx::UniformBufferPtr& ptr,
const T* data,
bool persistent = false) {
return emplaceOrUpdateUniformBuffer(ptr, data, sizeof(T), persistent);
}
#endif
protected:
virtual std::unique_ptr<TextureResource> createTextureResource(Size, TexturePixelType, TextureChannelDataType) = 0;
virtual std::unique_ptr<RenderbufferResource> createRenderbufferResource(RenderbufferPixelType, Size) = 0;
virtual std::unique_ptr<DrawScopeResource> createDrawScopeResource() = 0;
gfx::RenderingStats stats;
};
} // namespace gfx
} // namespace mbgl

View File

@@ -108,7 +108,7 @@ public:
template <class... Args>
void emplace_back(Args&&... args) {
static_assert(sizeof...(args) == groupSize, "wrong buffer element count");
static_assert(sizeof...(args) % groupSize == 0, "wrong buffer element count");
assert(!released);
util::ignore({(v.emplace_back(std::forward<Args>(args)), 0)...});
}

View File

@@ -63,10 +63,10 @@ public:
v(std::move(other.v)) {}
~VertexVector() override = default;
template <typename Arg>
void emplace_back(Arg&& vertex) {
template <class... Args>
void emplace_back(Args&&... args) {
assert(!released);
v.emplace_back(std::forward<Arg>(vertex));
util::ignore({(v.emplace_back(std::forward<Args>(args)), 0)...});
dirty = true;
}

View File

@@ -98,6 +98,7 @@ void RendererBackend::initShaders(gfx::ShaderRegistry& shaders, const ProgramPar
shaders::BuiltIn::CircleShader,
shaders::BuiltIn::CollisionBoxShader,
shaders::BuiltIn::CollisionCircleShader,
shaders::BuiltIn::CustomSymbolIconShader,
shaders::BuiltIn::DebugShader,
shaders::BuiltIn::FillShader,
shaders::BuiltIn::FillOutlineShader,

View File

@@ -17,6 +17,7 @@
#include <mbgl/shaders/mtl/clipping_mask.hpp>
#include <mbgl/shaders/mtl/collision_box.hpp>
#include <mbgl/shaders/mtl/collision_circle.hpp>
#include <mbgl/shaders/mtl/custom_symbol_icon.hpp>
#include <mbgl/shaders/mtl/debug.hpp>
#include <mbgl/shaders/mtl/fill.hpp>
#include <mbgl/shaders/mtl/fill_extrusion.hpp>
@@ -129,6 +130,7 @@ void RendererBackend::initShaders(gfx::ShaderRegistry& shaders, const ProgramPar
shaders::BuiltIn::ClippingMaskProgram,
shaders::BuiltIn::CollisionBoxShader,
shaders::BuiltIn::CollisionCircleShader,
shaders::BuiltIn::CustomSymbolIconShader,
shaders::BuiltIn::DebugShader,
shaders::BuiltIn::FillShader,
shaders::BuiltIn::FillOutlineShader,

View File

@@ -48,6 +48,15 @@ const std::vector<UniformBlockInfo> ShaderInfo<BuiltIn::CollisionBoxShader, gfx:
const std::vector<TextureInfo> ShaderInfo<BuiltIn::CollisionBoxShader, gfx::Backend::Type::OpenGL>::textures = {};
// Collision Circle
const std::vector<UniformBlockInfo>
ShaderInfo<BuiltIn::CustomSymbolIconShader, gfx::Backend::Type::OpenGL>::uniformBlocks = {
UniformBlockInfo{"CustomSymbolIconDrawableUBO", idCustomSymbolIconDrawableUBO},
UniformBlockInfo{"CustomSymbolIconParametersUBO", idCustomSymbolIconParametersUBO},
};
const std::vector<TextureInfo> ShaderInfo<BuiltIn::CustomSymbolIconShader, gfx::Backend::Type::OpenGL>::textures = {
TextureInfo{"u_texture", idSymbolImageTexture},
};
const std::vector<UniformBlockInfo>
ShaderInfo<BuiltIn::CollisionCircleShader, gfx::Backend::Type::OpenGL>::uniformBlocks = {
UniformBlockInfo{"CollisionCircleUBO", idCollisionUBO},

View File

@@ -0,0 +1,22 @@
#include <mbgl/shaders/mtl/custom_symbol_icon.hpp>
namespace mbgl {
namespace shaders {
const std::array<AttributeInfo, 2>
ShaderSource<BuiltIn::CustomSymbolIconShader, gfx::Backend::Type::Metal>::attributes = {
// always attributes
AttributeInfo{0, gfx::AttributeDataType::Float2, "a_pos"},
AttributeInfo{1, gfx::AttributeDataType::Float2, "a_tex"},
};
const std::array<UniformBlockInfo, 2>
ShaderSource<BuiltIn::CustomSymbolIconShader, gfx::Backend::Type::Metal>::uniforms = {
UniformBlockInfo{2, true, false, sizeof(CustomSymbolIconDrawableUBO), idCustomSymbolIconDrawableUBO},
UniformBlockInfo{3, true, false, sizeof(CustomSymbolIconParametersUBO), idCustomSymbolIconParametersUBO},
};
const std::array<TextureInfo, 1> ShaderSource<BuiltIn::CustomSymbolIconShader, gfx::Backend::Type::Metal>::textures = {
TextureInfo{0, idCustomSymbolIconTexture},
};
} // namespace shaders
} // namespace mbgl

View File

@@ -21,8 +21,10 @@
#include <mbgl/util/math.hpp>
#include <mbgl/tile/geometry_tile_data.hpp>
#include <mbgl/util/containers.hpp>
#include <mbgl/gfx/fill_generator.hpp>
#include <mbgl/shaders/custom_drawable_layer_ubo.hpp>
#include <cmath>
namespace mbgl {
@@ -176,6 +178,59 @@ private:
float opacity;
};
class SymbolDrawableTweaker : public gfx::DrawableTweaker {
public:
SymbolDrawableTweaker(const CustomDrawableLayerHost::Interface::SymbolOptions& options_)
: options(options_) {}
~SymbolDrawableTweaker() 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();
mat4 tileMatrix;
parameters.state.matrixFor(/*out*/ tileMatrix, tileID);
const auto matrix = LayerTweaker::getTileMatrix(
tileID, parameters, {{0, 0}}, style::TranslateAnchorType::Viewport, false, false, drawable, false);
const shaders::CustomSymbolIconDrawableUBO drawableUBO{/*matrix = */ util::cast<float>(matrix)};
const auto pixelsToTileUnits = tileID.pixelsToTileUnits(
1.0f, options.scaleWithMap ? tileID.canonical.z : parameters.state.getZoom());
const float factor = options.scaleWithMap
? static_cast<float>(std::pow(2.f, parameters.state.getZoom() - tileID.canonical.z))
: 1.0f;
const auto extrudeScale = options.pitchWithMap ? std::array<float, 2>{pixelsToTileUnits, pixelsToTileUnits}
: std::array<float, 2>{parameters.pixelsToGLUnits[0] * factor,
parameters.pixelsToGLUnits[1] * factor};
const shaders::CustomSymbolIconParametersUBO parametersUBO{
/*extrude_scale*/ {extrudeScale[0] * options.size.width, extrudeScale[1] * options.size.height},
/*anchor*/ options.anchor,
/*angle_degrees*/ options.angleDegrees,
/*scale_with_map*/ options.scaleWithMap,
/*pitch_with_map*/ options.pitchWithMap,
/*camera_to_center_distance*/ parameters.state.getCameraToCenterDistance(),
/*aspect_ratio*/ parameters.pixelsToGLUnits[0] / parameters.pixelsToGLUnits[1],
0,
0,
0};
// set UBOs
auto& uniforms = drawable.mutableUniformBuffers();
uniforms.createOrUpdate(idCustomSymbolIconDrawableUBO, &drawableUBO, parameters.context);
uniforms.createOrUpdate(idCustomSymbolIconParametersUBO, &parametersUBO, parameters.context);
};
private:
CustomDrawableLayerHost::Interface::SymbolOptions options;
};
CustomDrawableLayerHost::Interface::Interface(RenderLayer& layer_,
LayerGroupBasePtr& layerGroup_,
gfx::ShaderRegistry& shaders_,
@@ -220,10 +275,16 @@ void CustomDrawableLayerHost::Interface::setFillOptions(const FillOptions& optio
fillOptions = options;
}
void CustomDrawableLayerHost::Interface::setSymbolOptions(const SymbolOptions& options) {
finish();
symbolOptions = options;
}
void CustomDrawableLayerHost::Interface::addPolyline(const GeometryCoordinates& coordinates) {
if (!lineShader) lineShader = lineShaderDefault();
assert(lineShader);
if (!builder || builder->getShader() != lineShader) {
finish();
builder = createBuilder("lines", lineShader);
}
assert(builder);
@@ -235,6 +296,7 @@ void CustomDrawableLayerHost::Interface::addFill(const GeometryCollection& geome
if (!fillShader) fillShader = fillShaderDefault();
assert(fillShader);
if (!builder || builder->getShader() != fillShader) {
finish();
builder = createBuilder("fill", fillShader);
}
assert(builder);
@@ -274,15 +336,94 @@ void CustomDrawableLayerHost::Interface::addFill(const GeometryCollection& geome
builder->flush(context);
}
void CustomDrawableLayerHost::Interface::addSymbol(const GeometryCoordinate& point) {
if (!symbolShader) symbolShader = symbolShaderDefault();
assert(symbolShader);
if (!builder || builder->getShader() != symbolShader) {
finish();
builder = createBuilder("symbol", symbolShader);
}
assert(builder);
assert(builder->getShader() == symbolShader);
// temporary: buffers
struct CustomSymbolIcon {
std::array<float, 2> a_pos;
std::array<float, 2> a_tex;
};
// vertices
using VertexVector = gfx::VertexVector<CustomSymbolIcon>;
const std::shared_ptr<VertexVector> sharedVertices = std::make_shared<VertexVector>();
VertexVector& vertices = *sharedVertices;
// encode center and extrude direction into vertices
for (int y = 0; y <= 1; ++y) {
for (int x = 0; x <= 1; ++x) {
vertices.emplace_back(
CustomSymbolIcon{{static_cast<float>(point.x * 2 + x), static_cast<float>(point.y * 2 + y)},
{symbolOptions.textureCoordinates[x][0], symbolOptions.textureCoordinates[y][1]}});
}
}
// indexes
using TriangleIndexVector = gfx::IndexVector<gfx::Triangles>;
const std::shared_ptr<TriangleIndexVector> sharedTriangles = std::make_shared<TriangleIndexVector>();
TriangleIndexVector& triangles = *sharedTriangles;
triangles.emplace_back(0, 1, 2, 1, 2, 3);
SegmentVector<CustomSymbolIcon> triangleSegments;
triangleSegments.emplace_back(Segment<CustomSymbolIcon>{0, 0, 4, 6});
// add to builder
static const StringIdentity idPositionAttribName = stringIndexer().get("a_pos");
static const StringIdentity idTextureAttribName = stringIndexer().get("a_tex");
builder->setVertexAttrNameId(idPositionAttribName);
auto attrs = context.createVertexAttributeArray();
if (const auto& attr = attrs->add(idPositionAttribName)) {
attr->setSharedRawData(sharedVertices,
offsetof(CustomSymbolIcon, a_pos),
/*vertexOffset=*/0,
sizeof(CustomSymbolIcon),
gfx::AttributeDataType::Float2);
}
if (const auto& attr = attrs->add(idTextureAttribName)) {
attr->setSharedRawData(sharedVertices,
offsetof(CustomSymbolIcon, a_tex),
/*vertexOffset=*/0,
sizeof(CustomSymbolIcon),
gfx::AttributeDataType::Float2);
}
builder->setVertexAttributes(std::move(attrs));
builder->setRawVertices({}, vertices.elements(), gfx::AttributeDataType::Float2);
builder->setSegments(gfx::Triangles(), sharedTriangles, triangleSegments.data(), triangleSegments.size());
// texture
if (symbolOptions.texture) {
builder->setTexture(symbolOptions.texture, idCustomSymbolIconTexture);
}
// create fill tweaker
auto tweaker = std::make_shared<SymbolDrawableTweaker>(symbolOptions);
builder->addTweaker(tweaker);
// flush current builder drawable
builder->flush(context);
}
void CustomDrawableLayerHost::Interface::finish() {
if (builder && !builder->empty()) {
// finish
const auto finish_ = [this](auto& tweaker) {
const auto finish_ = [this](gfx::DrawableTweakerPtr tweaker) {
builder->flush(context);
for (auto& drawable : builder->clearDrawables()) {
assert(tileID.has_value());
drawable->setTileID(tileID.value());
drawable->addTweaker(tweaker);
if (tweaker) {
drawable->addTweaker(tweaker);
}
TileLayerGroup* tileLayerGroup = static_cast<TileLayerGroup*>(layerGroup.get());
tileLayerGroup->addDrawable(RenderPass::Translucent, tileID.value(), std::move(drawable));
@@ -314,12 +455,17 @@ void CustomDrawableLayerHost::Interface::finish() {
// finish drawables
finish_(tweaker);
} else if (builder->getShader() == symbolShader) {
// finish building symbols
// finish drawables
finish_(nullptr);
}
}
}
gfx::ShaderPtr CustomDrawableLayerHost::Interface::lineShaderDefault() const {
gfx::ShaderGroupPtr lineShaderGroup = shaders.getShaderGroup("LineShader");
gfx::ShaderGroupPtr shaderGroup = shaders.getShaderGroup("LineShader");
const mbgl::unordered_set<StringIdentity> propertiesAsUniforms{
stringIndexer().get("a_color"),
@@ -330,18 +476,22 @@ gfx::ShaderPtr CustomDrawableLayerHost::Interface::lineShaderDefault() const {
stringIndexer().get("a_width"),
};
return lineShaderGroup->getOrCreateShader(context, propertiesAsUniforms);
return shaderGroup->getOrCreateShader(context, propertiesAsUniforms);
}
gfx::ShaderPtr CustomDrawableLayerHost::Interface::fillShaderDefault() const {
gfx::ShaderGroupPtr fillShaderGroup = shaders.getShaderGroup("FillShader");
gfx::ShaderGroupPtr shaderGroup = shaders.getShaderGroup("FillShader");
const mbgl::unordered_set<StringIdentity> propertiesAsUniforms{
stringIndexer().get("a_color"),
stringIndexer().get("a_opacity"),
};
return fillShaderGroup->getOrCreateShader(context, propertiesAsUniforms);
return shaderGroup->getOrCreateShader(context, propertiesAsUniforms);
}
gfx::ShaderPtr CustomDrawableLayerHost::Interface::symbolShaderDefault() const {
return context.getGenericShader(shaders, "CustomSymbolIconShader");
}
std::unique_ptr<gfx::DrawableBuilder> CustomDrawableLayerHost::Interface::createBuilder(const std::string& name,