#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace mbgl { namespace style { namespace { const LayerTypeInfo typeInfoCustomDrawable{"custom-drawable", LayerTypeInfo::Source::NotRequired, LayerTypeInfo::Pass3D::NotRequired, LayerTypeInfo::Layout::NotRequired, LayerTypeInfo::FadingTiles::NotRequired, LayerTypeInfo::CrossTileIndex::NotRequired, LayerTypeInfo::TileKind::NotRequired}; } // namespace CustomDrawableLayer::CustomDrawableLayer(const std::string& layerID, std::unique_ptr host) : Layer(makeMutable(layerID, std::move(host))) {} CustomDrawableLayer::~CustomDrawableLayer() = default; const CustomDrawableLayer::Impl& CustomDrawableLayer::impl() const { return static_cast(*baseImpl); } Mutable CustomDrawableLayer::mutableImpl() const { return makeMutable(impl()); } std::unique_ptr CustomDrawableLayer::cloneRef(const std::string&) const { assert(false); return nullptr; } using namespace conversion; std::optional CustomDrawableLayer::setPropertyInternal(const std::string&, const Convertible&) { return Error{"layer doesn't support this property"}; } StyleProperty CustomDrawableLayer::getProperty(const std::string&) const { return {}; } Mutable CustomDrawableLayer::mutableBaseImpl() const { return staticMutableCast(mutableImpl()); } // static const LayerTypeInfo* CustomDrawableLayer::Impl::staticTypeInfo() noexcept { return &typeInfoCustomDrawable; } // CustomDrawableLayerHost::Interface class LineDrawableTweaker : public gfx::DrawableTweaker { public: LineDrawableTweaker(const shaders::LinePropertiesUBO& properties) : linePropertiesUBO(properties) {} ~LineDrawableTweaker() 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(); mat4 tileMatrix; parameters.state.matrixFor(/*out*/ tileMatrix, tileID); const auto matrix = LayerTweaker::getTileMatrix( tileID, parameters, {{0, 0}}, style::TranslateAnchorType::Viewport, false, false, false); static const StringIdentity idLineUBOName = stringIndexer().get("LineUBO"); const shaders::LineUBO lineUBO{ /*matrix = */ util::cast(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}; static const StringIdentity idLinePropertiesUBOName = stringIndexer().get("LinePropertiesUBO"); static const StringIdentity idLineInterpolationUBOName = stringIndexer().get("LineInterpolationUBO"); const shaders::LineInterpolationUBO lineInterpolationUBO{/*color_t =*/0.f, /*blur_t =*/0.f, /*opacity_t =*/0.f, /*gapwidth_t =*/0.f, /*offset_t =*/0.f, /*width_t =*/0.f, 0, 0}; auto& uniforms = drawable.mutableUniformBuffers(); uniforms.createOrUpdate(idLineUBOName, &lineUBO, parameters.context); uniforms.createOrUpdate(idLinePropertiesUBOName, &linePropertiesUBO, parameters.context); uniforms.createOrUpdate(idLineInterpolationUBOName, &lineInterpolationUBO, parameters.context); #if MLN_RENDER_BACKEND_METAL static const StringIdentity idExpressionInputsUBOName = stringIndexer().get("ExpressionInputsUBO"); const auto expressionUBO = LayerTweaker::buildExpressionUBO(zoom, parameters.frameCount); uniforms.createOrUpdate(idExpressionInputsUBOName, &expressionUBO, parameters.context); static const StringIdentity idLinePermutationUBOName = stringIndexer().get("LinePermutationUBO"); const shaders::LinePermutationUBO permutationUBO = { /* .color = */ {/*.source=*/shaders::AttributeSource::Constant, /*.expression=*/{}}, /* .blur = */ {/*.source=*/shaders::AttributeSource::Constant, /*.expression=*/{}}, /* .opacity = */ {/*.source=*/shaders::AttributeSource::Constant, /*.expression=*/{}}, /* .gapwidth = */ {/*.source=*/shaders::AttributeSource::Constant, /*.expression=*/{}}, /* .offset = */ {/*.source=*/shaders::AttributeSource::Constant, /*.expression=*/{}}, /* .width = */ {/*.source=*/shaders::AttributeSource::Constant, /*.expression=*/{}}, /* .floorwidth = */ {/*.source=*/shaders::AttributeSource::Constant, /*.expression=*/{}}, /* .pattern_from = */ {/*.source=*/shaders::AttributeSource::Constant, /*.expression=*/{}}, /* .pattern_to = */ {/*.source=*/shaders::AttributeSource::Constant, /*.expression=*/{}}, /* .overdrawInspector = */ false, /* .pad = */ 0, 0, 0, 0}; uniforms.createOrUpdate(idLinePermutationUBOName, &permutationUBO, parameters.context); #endif // MLN_RENDER_BACKEND_METAL }; private: shaders::LinePropertiesUBO linePropertiesUBO; }; class FillDrawableTweaker : public gfx::DrawableTweaker { public: FillDrawableTweaker(const Color& color_, float opacity_) : color(color_), opacity(opacity_) {} ~FillDrawableTweaker() 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, false); static const StringIdentity idFillDrawableUBOName = stringIndexer().get("FillDrawableUBO"); const shaders::FillDrawableUBO fillUBO{/*matrix = */ util::cast(matrix)}; static const StringIdentity idFillEvaluatedPropsUBOName = stringIndexer().get("FillEvaluatedPropsUBO"); const shaders::FillEvaluatedPropsUBO fillPropertiesUBO{ /* .color = */ color, /* .opacity = */ opacity, 0, 0, 0, }; static const StringIdentity idFillInterpolateUBOName = stringIndexer().get("FillInterpolateUBO"); const shaders::FillInterpolateUBO fillInterpolateUBO{ /* .color_t = */ 0.f, /* .opacity_t = */ 0.f, 0, 0, }; auto& uniforms = drawable.mutableUniformBuffers(); uniforms.createOrUpdate(idFillDrawableUBOName, &fillUBO, parameters.context); uniforms.createOrUpdate(idFillEvaluatedPropsUBOName, &fillPropertiesUBO, parameters.context); uniforms.createOrUpdate(idFillInterpolateUBOName, &fillInterpolateUBO, parameters.context); #if MLN_RENDER_BACKEND_METAL const auto zoom = parameters.state.getZoom(); static const StringIdentity idExpressionInputsUBOName = stringIndexer().get("ExpressionInputsUBO"); const auto expressionUBO = LayerTweaker::buildExpressionUBO(zoom, parameters.frameCount); uniforms.createOrUpdate(idExpressionInputsUBOName, &expressionUBO, parameters.context); static const StringIdentity idFillPermutationUBOName = stringIndexer().get("FillPermutationUBO"); const shaders::FillPermutationUBO permutationUBO = { /* .color = */ {/*.source=*/shaders::AttributeSource::Constant, /*.expression=*/{}}, /* .opacity = */ {/*.source=*/shaders::AttributeSource::Constant, /*.expression=*/{}}, /* .overdrawInspector = */ false, 0, 0, 0, 0, 0, 0, }; uniforms.createOrUpdate(idFillPermutationUBOName, &permutationUBO, parameters.context); #endif // MLN_RENDER_BACKEND_METAL }; private: Color color; float opacity; }; CustomDrawableLayerHost::Interface::Interface(RenderLayer& layer_, LayerGroupBasePtr& layerGroup_, gfx::ShaderRegistry& shaders_, gfx::Context& context_, const TransformState& state_, const std::shared_ptr& updateParameters_, const RenderTree& renderTree_, UniqueChangeRequestVec& changes_) : layer(layer_), layerGroup(layerGroup_), shaders(shaders_), context(context_), state(state_), updateParameters(updateParameters_), renderTree(renderTree_), changes(changes_) { // ensure we have a default layer group set up if (!layerGroup) { if (auto aLayerGroup = context.createTileLayerGroup( /*layerIndex*/ layer.getLayerIndex(), /*initialCapacity=*/64, layer.getID())) { changes.emplace_back(std::make_unique(aLayerGroup)); layerGroup = std::move(aLayerGroup); } } } std::size_t CustomDrawableLayerHost::Interface::getDrawableCount() const { return layerGroup->getDrawableCount(); } void CustomDrawableLayerHost::Interface::setTileID(OverscaledTileID tileID_) { tileID = tileID_; } void CustomDrawableLayerHost::Interface::setLineOptions(const LineOptions& options) { finish(); lineOptions = options; } void CustomDrawableLayerHost::Interface::setFillOptions(const FillOptions& options) { finish(); fillOptions = options; } void CustomDrawableLayerHost::Interface::addPolyline(const GeometryCoordinates& coordinates) { if (!lineShader) lineShader = lineShaderDefault(); assert(lineShader); if (!builder || builder->getShader() != lineShader) { builder = createBuilder("lines", lineShader); } assert(builder); assert(builder->getShader() == lineShader); builder->addPolyline(coordinates, lineOptions.geometry); } void CustomDrawableLayerHost::Interface::addFill(const GeometryCollection& geometry) { if (!fillShader) fillShader = fillShaderDefault(); assert(fillShader); if (!builder || builder->getShader() != fillShader) { builder = createBuilder("fill", fillShader); } assert(builder); assert(builder->getShader() == fillShader); // provision buffers for fill vertices, indexes and segments using VertexVector = gfx::VertexVector; const std::shared_ptr sharedVertices = std::make_shared(); VertexVector& vertices = *sharedVertices; using TriangleIndexVector = gfx::IndexVector; const std::shared_ptr sharedTriangles = std::make_shared(); TriangleIndexVector& triangles = *sharedTriangles; SegmentVector triangleSegments; // generate fill geometry into buffers gfx::generateFillBuffers(geometry, vertices, triangles, triangleSegments); // add to builder static const StringIdentity idVertexAttribName = stringIndexer().get("a_pos"); builder->setVertexAttrNameId(idVertexAttribName); auto attrs = context.createVertexAttributeArray(); if (const auto& attr = attrs->add(idVertexAttribName)) { attr->setSharedRawData(sharedVertices, offsetof(FillLayoutVertex, a1), /*vertexOffset=*/0, sizeof(FillLayoutVertex), gfx::AttributeDataType::Short2); } builder->setVertexAttributes(std::move(attrs)); builder->setRawVertices({}, vertices.elements(), gfx::AttributeDataType::Short2); builder->setSegments(gfx::Triangles(), sharedTriangles, triangleSegments.data(), triangleSegments.size()); // flush current builder drawable builder->flush(context); } void CustomDrawableLayerHost::Interface::finish() { if (builder && !builder->empty()) { // finish const auto finish_ = [this](auto& tweaker) { builder->flush(context); for (auto& drawable : builder->clearDrawables()) { assert(tileID.has_value()); drawable->setTileID(tileID.value()); drawable->addTweaker(tweaker); TileLayerGroup* tileLayerGroup = static_cast(layerGroup.get()); tileLayerGroup->addDrawable(RenderPass::Translucent, tileID.value(), std::move(drawable)); } }; if (builder->getShader() == lineShader) { // finish building lines // create line tweaker const shaders::LinePropertiesUBO linePropertiesUBO{lineOptions.color, lineOptions.blur, lineOptions.opacity, lineOptions.gapWidth, lineOptions.offset, lineOptions.width, 0, 0, 0}; auto tweaker = std::make_shared(linePropertiesUBO); // finish drawables finish_(tweaker); } else if (builder->getShader() == fillShader) { // finish building fills // create fill tweaker auto tweaker = std::make_shared(fillOptions.color, fillOptions.opacity); // finish drawables finish_(tweaker); } } } gfx::ShaderPtr CustomDrawableLayerHost::Interface::lineShaderDefault() const { gfx::ShaderGroupPtr lineShaderGroup = shaders.getShaderGroup("LineShader"); const mbgl::unordered_set propertiesAsUniforms{ stringIndexer().get("a_color"), stringIndexer().get("a_blur"), stringIndexer().get("a_opacity"), stringIndexer().get("a_gapwidth"), stringIndexer().get("a_offset"), stringIndexer().get("a_width"), }; return lineShaderGroup->getOrCreateShader(context, propertiesAsUniforms); } gfx::ShaderPtr CustomDrawableLayerHost::Interface::fillShaderDefault() const { gfx::ShaderGroupPtr fillShaderGroup = shaders.getShaderGroup("FillShader"); const mbgl::unordered_set propertiesAsUniforms{ stringIndexer().get("a_color"), stringIndexer().get("a_opacity"), }; return fillShaderGroup->getOrCreateShader(context, propertiesAsUniforms); } std::unique_ptr CustomDrawableLayerHost::Interface::createBuilder(const std::string& name, gfx::ShaderPtr shader) const { std::unique_ptr builder_ = context.createDrawableBuilder(name); builder_->setShader(std::static_pointer_cast(shader)); builder_->setSubLayerIndex(0); builder_->setEnableDepth(false); builder_->setColorMode(gfx::ColorMode::alphaBlended()); builder_->setCullFaceMode(gfx::CullFaceMode::disabled()); builder_->setRenderPass(RenderPass::Translucent); return builder_; } } // namespace style } // namespace mbgl