Files
MapLibreParticleLab/src/mbgl/style/layers/custom_drawable_layer.cpp
2023-11-29 02:57:51 +00:00

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