407 lines
17 KiB
C++
407 lines
17 KiB
C++
#include <mbgl/style/layers/custom_drawable_layer.hpp>
|
|
#include <mbgl/style/layers/custom_drawable_layer_impl.hpp>
|
|
|
|
#include <mbgl/renderer/layers/render_custom_drawable_layer.hpp>
|
|
#include <mbgl/style/layer_observer.hpp>
|
|
#include <mbgl/gfx/context.hpp>
|
|
#include <mbgl/renderer/change_request.hpp>
|
|
#include <mbgl/renderer/layer_group.hpp>
|
|
#include <mbgl/gfx/cull_face_mode.hpp>
|
|
#include <mbgl/shaders/shader_program_base.hpp>
|
|
#include <mbgl/renderer/layer_tweaker.hpp>
|
|
#include <mbgl/gfx/drawable.hpp>
|
|
#include <mbgl/gfx/drawable_tweaker.hpp>
|
|
#include <mbgl/shaders/line_layer_ubo.hpp>
|
|
#include <mbgl/util/string_indexer.hpp>
|
|
#include <mbgl/util/convert.hpp>
|
|
#include <mbgl/util/geometry.hpp>
|
|
#include <mbgl/programs/fill_program.hpp>
|
|
#include <mbgl/shaders/fill_layer_ubo.hpp>
|
|
#include <mbgl/util/math.hpp>
|
|
#include <mbgl/tile/geometry_tile_data.hpp>
|
|
#include <mbgl/util/containers.hpp>
|
|
|
|
#include <mbgl/gfx/fill_generator.hpp>
|
|
|
|
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<CustomDrawableLayerHost> host)
|
|
: Layer(makeMutable<Impl>(layerID, std::move(host))) {}
|
|
|
|
CustomDrawableLayer::~CustomDrawableLayer() = default;
|
|
|
|
const CustomDrawableLayer::Impl& CustomDrawableLayer::impl() const {
|
|
return static_cast<const Impl&>(*baseImpl);
|
|
}
|
|
|
|
Mutable<CustomDrawableLayer::Impl> CustomDrawableLayer::mutableImpl() const {
|
|
return makeMutable<Impl>(impl());
|
|
}
|
|
|
|
std::unique_ptr<Layer> CustomDrawableLayer::cloneRef(const std::string&) const {
|
|
assert(false);
|
|
return nullptr;
|
|
}
|
|
|
|
using namespace conversion;
|
|
|
|
std::optional<Error> CustomDrawableLayer::setPropertyInternal(const std::string&, const Convertible&) {
|
|
return Error{"layer doesn't support this property"};
|
|
}
|
|
|
|
StyleProperty CustomDrawableLayer::getProperty(const std::string&) const {
|
|
return {};
|
|
}
|
|
|
|
Mutable<Layer::Impl> CustomDrawableLayer::mutableBaseImpl() const {
|
|
return staticMutableCast<Layer::Impl>(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<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};
|
|
|
|
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<float>(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>& 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<AddLayerGroupRequest>(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<FillLayoutVertex>;
|
|
const std::shared_ptr<VertexVector> sharedVertices = std::make_shared<VertexVector>();
|
|
VertexVector& vertices = *sharedVertices;
|
|
|
|
using TriangleIndexVector = gfx::IndexVector<gfx::Triangles>;
|
|
const std::shared_ptr<TriangleIndexVector> sharedTriangles = std::make_shared<TriangleIndexVector>();
|
|
TriangleIndexVector& triangles = *sharedTriangles;
|
|
|
|
SegmentVector<FillAttributes> 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<TileLayerGroup*>(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<LineDrawableTweaker>(linePropertiesUBO);
|
|
|
|
// finish drawables
|
|
finish_(tweaker);
|
|
} else if (builder->getShader() == fillShader) {
|
|
// finish building fills
|
|
|
|
// create fill tweaker
|
|
auto tweaker = std::make_shared<FillDrawableTweaker>(fillOptions.color, fillOptions.opacity);
|
|
|
|
// finish drawables
|
|
finish_(tweaker);
|
|
}
|
|
}
|
|
}
|
|
|
|
gfx::ShaderPtr CustomDrawableLayerHost::Interface::lineShaderDefault() const {
|
|
gfx::ShaderGroupPtr lineShaderGroup = shaders.getShaderGroup("LineShader");
|
|
|
|
const mbgl::unordered_set<StringIdentity> 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<StringIdentity> propertiesAsUniforms{
|
|
stringIndexer().get("a_color"),
|
|
stringIndexer().get("a_opacity"),
|
|
};
|
|
|
|
return fillShaderGroup->getOrCreateShader(context, propertiesAsUniforms);
|
|
}
|
|
|
|
std::unique_ptr<gfx::DrawableBuilder> CustomDrawableLayerHost::Interface::createBuilder(const std::string& name,
|
|
gfx::ShaderPtr shader) const {
|
|
std::unique_ptr<gfx::DrawableBuilder> builder_ = context.createDrawableBuilder(name);
|
|
builder_->setShader(std::static_pointer_cast<gfx::ShaderProgramBase>(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
|