summaryrefslogtreecommitdiff
path: root/src/mbgl/renderer/layers
diff options
context:
space:
mode:
Diffstat (limited to 'src/mbgl/renderer/layers')
-rw-r--r--src/mbgl/renderer/layers/render_background_layer.cpp115
-rw-r--r--src/mbgl/renderer/layers/render_background_layer.hpp33
-rw-r--r--src/mbgl/renderer/layers/render_circle_layer.cpp120
-rw-r--r--src/mbgl/renderer/layers/render_circle_layer.hpp40
-rw-r--r--src/mbgl/renderer/layers/render_custom_layer.cpp81
-rw-r--r--src/mbgl/renderer/layers/render_custom_layer.hpp36
-rw-r--r--src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp167
-rw-r--r--src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp44
-rw-r--r--src/mbgl/renderer/layers/render_fill_layer.cpp205
-rw-r--r--src/mbgl/renderer/layers/render_fill_layer.hpp40
-rw-r--r--src/mbgl/renderer/layers/render_line_layer.cpp204
-rw-r--r--src/mbgl/renderer/layers/render_line_layer.hpp52
-rw-r--r--src/mbgl/renderer/layers/render_raster_layer.cpp155
-rw-r--r--src/mbgl/renderer/layers/render_raster_layer.hpp34
-rw-r--r--src/mbgl/renderer/layers/render_symbol_layer.cpp325
-rw-r--r--src/mbgl/renderer/layers/render_symbol_layer.hpp99
16 files changed, 1750 insertions, 0 deletions
diff --git a/src/mbgl/renderer/layers/render_background_layer.cpp b/src/mbgl/renderer/layers/render_background_layer.cpp
new file mode 100644
index 0000000000..9fddba3f74
--- /dev/null
+++ b/src/mbgl/renderer/layers/render_background_layer.cpp
@@ -0,0 +1,115 @@
+#include <mbgl/renderer/layers/render_background_layer.hpp>
+#include <mbgl/style/layers/background_layer_impl.hpp>
+#include <mbgl/renderer/bucket.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
+#include <mbgl/renderer/image_manager.hpp>
+#include <mbgl/renderer/render_static_data.hpp>
+#include <mbgl/programs/programs.hpp>
+#include <mbgl/programs/fill_program.hpp>
+#include <mbgl/util/tile_cover.hpp>
+
+namespace mbgl {
+
+using namespace style;
+
+RenderBackgroundLayer::RenderBackgroundLayer(Immutable<style::BackgroundLayer::Impl> _impl)
+ : RenderLayer(style::LayerType::Background, _impl),
+ unevaluated(impl().paint.untransitioned()) {
+}
+
+const style::BackgroundLayer::Impl& RenderBackgroundLayer::impl() const {
+ return static_cast<const style::BackgroundLayer::Impl&>(*baseImpl);
+}
+
+std::unique_ptr<Bucket> RenderBackgroundLayer::createBucket(const BucketParameters &,
+ const std::vector<const RenderLayer *> &) const {
+ assert(false);
+ return nullptr;
+}
+
+void RenderBackgroundLayer::transition(const TransitionParameters &parameters) {
+ unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated));
+}
+
+void RenderBackgroundLayer::evaluate(const PropertyEvaluationParameters &parameters) {
+ evaluated = unevaluated.evaluate(parameters);
+
+ passes = evaluated.get<style::BackgroundOpacity>() > 0 ? RenderPass::Translucent
+ : RenderPass::None;
+}
+
+bool RenderBackgroundLayer::hasTransition() const {
+ return unevaluated.hasTransition();
+}
+
+void RenderBackgroundLayer::render(PaintParameters& parameters, RenderSource*) {
+ // Note that for bottommost layers without a pattern, the background color is drawn with
+ // glClear rather than this method.
+
+ style::FillPaintProperties::PossiblyEvaluated properties;
+ properties.get<FillPattern>() = evaluated.get<BackgroundPattern>();
+ properties.get<FillOpacity>() = { evaluated.get<BackgroundOpacity>() };
+ properties.get<FillColor>() = { evaluated.get<BackgroundColor>() };
+
+ const FillProgram::PaintPropertyBinders paintAttibuteData(properties, 0);
+
+ if (!evaluated.get<BackgroundPattern>().to.empty()) {
+ optional<ImagePosition> imagePosA = parameters.imageManager.getPattern(evaluated.get<BackgroundPattern>().from);
+ optional<ImagePosition> imagePosB = parameters.imageManager.getPattern(evaluated.get<BackgroundPattern>().to);
+
+ if (!imagePosA || !imagePosB)
+ return;
+
+ parameters.imageManager.bind(parameters.context, 0);
+
+ for (const auto& tileID : util::tileCover(parameters.state, parameters.state.getIntegerZoom())) {
+ parameters.programs.fillPattern.get(properties).draw(
+ parameters.context,
+ gl::Triangles(),
+ parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly),
+ gl::StencilMode::disabled(),
+ parameters.colorModeForRenderPass(),
+ FillPatternUniforms::values(
+ parameters.matrixForTile(tileID),
+ parameters.context.viewport.getCurrentValue().size,
+ parameters.imageManager.getPixelSize(),
+ *imagePosA,
+ *imagePosB,
+ evaluated.get<BackgroundPattern>(),
+ tileID,
+ parameters.state
+ ),
+ parameters.staticData.tileVertexBuffer,
+ parameters.staticData.quadTriangleIndexBuffer,
+ parameters.staticData.tileTriangleSegments,
+ paintAttibuteData,
+ properties,
+ parameters.state.getZoom(),
+ getID()
+ );
+ }
+ } else {
+ for (const auto& tileID : util::tileCover(parameters.state, parameters.state.getIntegerZoom())) {
+ parameters.programs.fill.get(properties).draw(
+ parameters.context,
+ gl::Triangles(),
+ parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly),
+ gl::StencilMode::disabled(),
+ parameters.colorModeForRenderPass(),
+ FillProgram::UniformValues {
+ uniforms::u_matrix::Value{ parameters.matrixForTile(tileID) },
+ uniforms::u_world::Value{ parameters.context.viewport.getCurrentValue().size },
+ },
+ parameters.staticData.tileVertexBuffer,
+ parameters.staticData.quadTriangleIndexBuffer,
+ parameters.staticData.tileTriangleSegments,
+ paintAttibuteData,
+ properties,
+ parameters.state.getZoom(),
+ getID()
+ );
+ }
+ }
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/layers/render_background_layer.hpp b/src/mbgl/renderer/layers/render_background_layer.hpp
new file mode 100644
index 0000000000..a619670ee4
--- /dev/null
+++ b/src/mbgl/renderer/layers/render_background_layer.hpp
@@ -0,0 +1,33 @@
+#pragma once
+
+#include <mbgl/renderer/render_layer.hpp>
+#include <mbgl/style/layers/background_layer_impl.hpp>
+#include <mbgl/style/layers/background_layer_properties.hpp>
+
+namespace mbgl {
+
+class RenderBackgroundLayer: public RenderLayer {
+public:
+ RenderBackgroundLayer(Immutable<style::BackgroundLayer::Impl>);
+ ~RenderBackgroundLayer() final = default;
+
+ void transition(const TransitionParameters&) override;
+ void evaluate(const PropertyEvaluationParameters&) override;
+ bool hasTransition() const override;
+ void render(PaintParameters&, RenderSource*) override;
+
+ std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override;
+
+ // Paint properties
+ style::BackgroundPaintProperties::Unevaluated unevaluated;
+ style::BackgroundPaintProperties::PossiblyEvaluated evaluated;
+
+ const style::BackgroundLayer::Impl& impl() const;
+};
+
+template <>
+inline bool RenderLayer::is<RenderBackgroundLayer>() const {
+ return type == style::LayerType::Background;
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/layers/render_circle_layer.cpp b/src/mbgl/renderer/layers/render_circle_layer.cpp
new file mode 100644
index 0000000000..e7b022f3ee
--- /dev/null
+++ b/src/mbgl/renderer/layers/render_circle_layer.cpp
@@ -0,0 +1,120 @@
+#include <mbgl/renderer/layers/render_circle_layer.hpp>
+#include <mbgl/renderer/buckets/circle_bucket.hpp>
+#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
+#include <mbgl/programs/programs.hpp>
+#include <mbgl/programs/circle_program.hpp>
+#include <mbgl/tile/tile.hpp>
+#include <mbgl/style/layers/circle_layer_impl.hpp>
+#include <mbgl/geometry/feature_index.hpp>
+#include <mbgl/util/math.hpp>
+#include <mbgl/util/intersection_tests.hpp>
+
+namespace mbgl {
+
+using namespace style;
+
+RenderCircleLayer::RenderCircleLayer(Immutable<style::CircleLayer::Impl> _impl)
+ : RenderLayer(style::LayerType::Circle, _impl),
+ unevaluated(impl().paint.untransitioned()) {
+}
+
+const style::CircleLayer::Impl& RenderCircleLayer::impl() const {
+ return static_cast<const style::CircleLayer::Impl&>(*baseImpl);
+}
+
+std::unique_ptr<Bucket> RenderCircleLayer::createBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) const {
+ return std::make_unique<CircleBucket>(parameters, layers);
+}
+
+void RenderCircleLayer::transition(const TransitionParameters& parameters) {
+ unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated));
+}
+
+void RenderCircleLayer::evaluate(const PropertyEvaluationParameters& parameters) {
+ evaluated = unevaluated.evaluate(parameters);
+
+ passes = ((evaluated.get<style::CircleRadius>().constantOr(1) > 0 ||
+ evaluated.get<style::CircleStrokeWidth>().constantOr(1) > 0)
+ && (evaluated.get<style::CircleColor>().constantOr(Color::black()).a > 0 ||
+ evaluated.get<style::CircleStrokeColor>().constantOr(Color::black()).a > 0)
+ && (evaluated.get<style::CircleOpacity>().constantOr(1) > 0 ||
+ evaluated.get<style::CircleStrokeOpacity>().constantOr(1) > 0))
+ ? RenderPass::Translucent : RenderPass::None;
+}
+
+bool RenderCircleLayer::hasTransition() const {
+ return unevaluated.hasTransition();
+}
+
+void RenderCircleLayer::render(PaintParameters& parameters, RenderSource*) {
+ if (parameters.pass == RenderPass::Opaque) {
+ return;
+ }
+
+ const bool scaleWithMap = evaluated.get<CirclePitchScale>() == CirclePitchScaleType::Map;
+ const bool pitchWithMap = evaluated.get<CirclePitchAlignment>() == AlignmentType::Map;
+
+ for (const RenderTile& tile : renderTiles) {
+ assert(dynamic_cast<CircleBucket*>(tile.tile.getBucket(*baseImpl)));
+ CircleBucket& bucket = *reinterpret_cast<CircleBucket*>(tile.tile.getBucket(*baseImpl));
+
+ parameters.programs.circle.get(evaluated).draw(
+ parameters.context,
+ gl::Triangles(),
+ parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly),
+ parameters.mapMode == MapMode::Still
+ ? parameters.stencilModeForClipping(tile.clip)
+ : gl::StencilMode::disabled(),
+ parameters.colorModeForRenderPass(),
+ CircleProgram::UniformValues {
+ uniforms::u_matrix::Value{
+ tile.translatedMatrix(evaluated.get<CircleTranslate>(),
+ evaluated.get<CircleTranslateAnchor>(),
+ parameters.state)
+ },
+ uniforms::u_scale_with_map::Value{ scaleWithMap },
+ uniforms::u_extrude_scale::Value{ pitchWithMap
+ ? std::array<float, 2> {{
+ tile.id.pixelsToTileUnits(1, parameters.state.getZoom()),
+ tile.id.pixelsToTileUnits(1, parameters.state.getZoom()) }}
+ : parameters.pixelsToGLUnits },
+ uniforms::u_camera_to_center_distance::Value{ parameters.state.getCameraToCenterDistance() },
+ uniforms::u_pitch_with_map::Value{ pitchWithMap }
+ },
+ *bucket.vertexBuffer,
+ *bucket.indexBuffer,
+ bucket.segments,
+ bucket.paintPropertyBinders.at(getID()),
+ evaluated,
+ parameters.state.getZoom(),
+ getID()
+ );
+ }
+}
+
+bool RenderCircleLayer::queryIntersectsFeature(
+ const GeometryCoordinates& queryGeometry,
+ const GeometryTileFeature& feature,
+ const float zoom,
+ const float bearing,
+ const float pixelsToTileUnits) const {
+
+ // Translate query geometry
+ auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry(
+ queryGeometry,
+ evaluated.get<style::CircleTranslate>(),
+ evaluated.get<style::CircleTranslateAnchor>(),
+ bearing,
+ pixelsToTileUnits);
+
+ // Evaluate function
+ auto circleRadius = evaluated.get<style::CircleRadius>()
+ .evaluate(feature, zoom, style::CircleRadius::defaultValue())
+ * pixelsToTileUnits;
+
+ // Test intersection
+ return util::polygonIntersectsBufferedMultiPoint(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries(), circleRadius);
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/layers/render_circle_layer.hpp b/src/mbgl/renderer/layers/render_circle_layer.hpp
new file mode 100644
index 0000000000..f31715f98f
--- /dev/null
+++ b/src/mbgl/renderer/layers/render_circle_layer.hpp
@@ -0,0 +1,40 @@
+#pragma once
+
+#include <mbgl/renderer/render_layer.hpp>
+#include <mbgl/style/layers/circle_layer_impl.hpp>
+#include <mbgl/style/layers/circle_layer_properties.hpp>
+
+namespace mbgl {
+
+class RenderCircleLayer: public RenderLayer {
+public:
+ RenderCircleLayer(Immutable<style::CircleLayer::Impl>);
+ ~RenderCircleLayer() final = default;
+
+ void transition(const TransitionParameters&) override;
+ void evaluate(const PropertyEvaluationParameters&) override;
+ bool hasTransition() const override;
+ void render(PaintParameters&, RenderSource*) override;
+
+ bool queryIntersectsFeature(
+ const GeometryCoordinates&,
+ const GeometryTileFeature&,
+ const float,
+ const float,
+ const float) const override;
+
+ std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override;
+
+ // Paint properties
+ style::CirclePaintProperties::Unevaluated unevaluated;
+ style::CirclePaintProperties::PossiblyEvaluated evaluated;
+
+ const style::CircleLayer::Impl& impl() const;
+};
+
+template <>
+inline bool RenderLayer::is<RenderCircleLayer>() const {
+ return type == style::LayerType::Circle;
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/layers/render_custom_layer.cpp b/src/mbgl/renderer/layers/render_custom_layer.cpp
new file mode 100644
index 0000000000..7ece3970da
--- /dev/null
+++ b/src/mbgl/renderer/layers/render_custom_layer.cpp
@@ -0,0 +1,81 @@
+#include <mbgl/renderer/layers/render_custom_layer.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
+#include <mbgl/renderer/backend_scope.hpp>
+#include <mbgl/renderer/renderer_backend.hpp>
+#include <mbgl/renderer/bucket.hpp>
+#include <mbgl/style/layers/custom_layer_impl.hpp>
+#include <mbgl/map/transform_state.hpp>
+
+namespace mbgl {
+
+using namespace style;
+
+RenderCustomLayer::RenderCustomLayer(Immutable<style::CustomLayer::Impl> _impl)
+ : RenderLayer(LayerType::Custom, _impl) {
+}
+
+RenderCustomLayer::~RenderCustomLayer() {
+ assert(BackendScope::exists());
+ if (initialized) {
+ if (contextDestroyed && impl().contextLostFn ) {
+ impl().contextLostFn(impl().context);
+ } else if (!contextDestroyed && impl().deinitializeFn) {
+ impl().deinitializeFn(impl().context);
+ }
+ }
+}
+
+const CustomLayer::Impl& RenderCustomLayer::impl() const {
+ return static_cast<const CustomLayer::Impl&>(*baseImpl);
+}
+
+void RenderCustomLayer::evaluate(const PropertyEvaluationParameters&) {
+ passes = RenderPass::Translucent;
+}
+
+bool RenderCustomLayer::hasTransition() const {
+ return false;
+}
+
+std::unique_ptr<Bucket> RenderCustomLayer::createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const {
+ assert(false);
+ return nullptr;
+}
+
+void RenderCustomLayer::render(PaintParameters& paintParameters, RenderSource*) {
+ if (!initialized) {
+ assert(impl().initializeFn);
+ impl().initializeFn(impl().context);
+ initialized = true;
+ }
+
+ gl::Context& context = paintParameters.context;
+ const TransformState& state = paintParameters.state;
+
+ // Reset GL state to a known state so the CustomLayer always has a clean slate.
+ context.bindVertexArray = 0;
+ context.setDepthMode(paintParameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly));
+ context.setStencilMode(gl::StencilMode::disabled());
+ context.setColorMode(paintParameters.colorModeForRenderPass());
+
+ CustomLayerRenderParameters parameters;
+
+ parameters.width = state.getSize().width;
+ parameters.height = state.getSize().height;
+ parameters.latitude = state.getLatLng().latitude();
+ parameters.longitude = state.getLatLng().longitude();
+ parameters.zoom = state.getZoom();
+ parameters.bearing = -state.getAngle() * util::RAD2DEG;
+ parameters.pitch = state.getPitch();
+ parameters.fieldOfView = state.getFieldOfView();
+
+ assert(impl().renderFn);
+ impl().renderFn(impl().context, parameters);
+
+ // Reset the view back to our original one, just in case the CustomLayer changed
+ // the viewport or Framebuffer.
+ paintParameters.backend.bind();
+ context.setDirtyState();
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/layers/render_custom_layer.hpp b/src/mbgl/renderer/layers/render_custom_layer.hpp
new file mode 100644
index 0000000000..32ed9da8da
--- /dev/null
+++ b/src/mbgl/renderer/layers/render_custom_layer.hpp
@@ -0,0 +1,36 @@
+#pragma once
+
+#include <mbgl/renderer/render_layer.hpp>
+#include <mbgl/style/layers/custom_layer_impl.hpp>
+
+namespace mbgl {
+
+class RenderCustomLayer: public RenderLayer {
+public:
+ RenderCustomLayer(Immutable<style::CustomLayer::Impl>);
+ ~RenderCustomLayer() final;
+
+ void transition(const TransitionParameters&) final {}
+ void evaluate(const PropertyEvaluationParameters&) override;
+ bool hasTransition() const override;
+
+ std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const final;
+ void render(PaintParameters&, RenderSource*) final;
+
+ const style::CustomLayer::Impl& impl() const;
+
+ void markContextDestroyed() {
+ contextDestroyed = true;
+ };
+
+private:
+ bool initialized = false;
+ bool contextDestroyed = false;
+};
+
+template <>
+inline bool RenderLayer::is<RenderCustomLayer>() const {
+ return type == style::LayerType::Custom;
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp
new file mode 100644
index 0000000000..fbd6160e8a
--- /dev/null
+++ b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp
@@ -0,0 +1,167 @@
+#include <mbgl/renderer/layers/render_fill_extrusion_layer.hpp>
+#include <mbgl/renderer/buckets/fill_extrusion_bucket.hpp>
+#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
+#include <mbgl/renderer/image_manager.hpp>
+#include <mbgl/renderer/render_static_data.hpp>
+#include <mbgl/renderer/renderer_backend.hpp>
+#include <mbgl/programs/programs.hpp>
+#include <mbgl/programs/fill_extrusion_program.hpp>
+#include <mbgl/tile/tile.hpp>
+#include <mbgl/style/layers/fill_extrusion_layer_impl.hpp>
+#include <mbgl/geometry/feature_index.hpp>
+#include <mbgl/util/math.hpp>
+#include <mbgl/util/intersection_tests.hpp>
+
+namespace mbgl {
+
+using namespace style;
+
+RenderFillExtrusionLayer::RenderFillExtrusionLayer(Immutable<style::FillExtrusionLayer::Impl> _impl)
+ : RenderLayer(style::LayerType::FillExtrusion, _impl),
+ unevaluated(impl().paint.untransitioned()) {
+}
+
+const style::FillExtrusionLayer::Impl& RenderFillExtrusionLayer::impl() const {
+ return static_cast<const style::FillExtrusionLayer::Impl&>(*baseImpl);
+}
+
+std::unique_ptr<Bucket> RenderFillExtrusionLayer::createBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) const {
+ return std::make_unique<FillExtrusionBucket>(parameters, layers);
+}
+
+void RenderFillExtrusionLayer::transition(const TransitionParameters& parameters) {
+ unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated));
+}
+
+void RenderFillExtrusionLayer::evaluate(const PropertyEvaluationParameters& parameters) {
+ evaluated = unevaluated.evaluate(parameters);
+
+ passes = (evaluated.get<style::FillExtrusionOpacity>() > 0)
+ ? (RenderPass::Translucent | RenderPass::Pass3D)
+ : RenderPass::None;
+}
+
+bool RenderFillExtrusionLayer::hasTransition() const {
+ return unevaluated.hasTransition();
+}
+
+void RenderFillExtrusionLayer::render(PaintParameters& parameters, RenderSource*) {
+ if (parameters.pass == RenderPass::Opaque) {
+ return;
+ }
+
+ if (parameters.pass == RenderPass::Pass3D) {
+ const auto& size = parameters.staticData.backendSize;
+
+ if (!renderTexture || renderTexture->getSize() != size) {
+ renderTexture = OffscreenTexture(parameters.context, size, *parameters.staticData.depthRenderbuffer);
+ }
+
+ renderTexture->bind();
+
+ optional<float> depthClearValue = {};
+ if (parameters.staticData.depthRenderbuffer->needsClearing()) depthClearValue = 1.0;
+ // Flag the depth buffer as no longer needing to be cleared for the remainder of this pass.
+ parameters.staticData.depthRenderbuffer->shouldClear(false);
+
+ parameters.context.setStencilMode(gl::StencilMode::disabled());
+ parameters.context.clear(Color{ 0.0f, 0.0f, 0.0f, 0.0f }, depthClearValue, {});
+
+ if (evaluated.get<FillExtrusionPattern>().from.empty()) {
+ for (const RenderTile& tile : renderTiles) {
+ assert(dynamic_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl)));
+ FillExtrusionBucket& bucket =
+ *reinterpret_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl));
+
+ parameters.programs.fillExtrusion.get(evaluated).draw(
+ parameters.context, gl::Triangles(),
+ parameters.depthModeFor3D(gl::DepthMode::ReadWrite),
+ gl::StencilMode::disabled(), parameters.colorModeForRenderPass(),
+ FillExtrusionUniforms::values(
+ tile.translatedClipMatrix(evaluated.get<FillExtrusionTranslate>(),
+ evaluated.get<FillExtrusionTranslateAnchor>(),
+ parameters.state),
+ parameters.state, parameters.evaluatedLight),
+ *bucket.vertexBuffer, *bucket.indexBuffer, bucket.triangleSegments,
+ bucket.paintPropertyBinders.at(getID()), evaluated, parameters.state.getZoom(),
+ getID());
+ }
+ } else {
+ optional<ImagePosition> imagePosA =
+ parameters.imageManager.getPattern(evaluated.get<FillExtrusionPattern>().from);
+ optional<ImagePosition> imagePosB =
+ parameters.imageManager.getPattern(evaluated.get<FillExtrusionPattern>().to);
+
+ if (!imagePosA || !imagePosB) {
+ return;
+ }
+
+ parameters.imageManager.bind(parameters.context, 0);
+
+ for (const RenderTile& tile : renderTiles) {
+ assert(dynamic_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl)));
+ FillExtrusionBucket& bucket =
+ *reinterpret_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl));
+
+ parameters.programs.fillExtrusionPattern.get(evaluated).draw(
+ parameters.context, gl::Triangles(),
+ parameters.depthModeFor3D(gl::DepthMode::ReadWrite),
+ gl::StencilMode::disabled(), parameters.colorModeForRenderPass(),
+ FillExtrusionPatternUniforms::values(
+ tile.translatedClipMatrix(evaluated.get<FillExtrusionTranslate>(),
+ evaluated.get<FillExtrusionTranslateAnchor>(),
+ parameters.state),
+ parameters.imageManager.getPixelSize(), *imagePosA, *imagePosB,
+ evaluated.get<FillExtrusionPattern>(), tile.id, parameters.state,
+ -std::pow(2, tile.id.canonical.z) / util::tileSize / 8.0f,
+ parameters.evaluatedLight),
+ *bucket.vertexBuffer, *bucket.indexBuffer, bucket.triangleSegments,
+ bucket.paintPropertyBinders.at(getID()), evaluated, parameters.state.getZoom(),
+ getID());
+ }
+ }
+
+ } else if (parameters.pass == RenderPass::Translucent) {
+ parameters.context.bindTexture(renderTexture->getTexture());
+
+ const auto& size = parameters.staticData.backendSize;
+
+ mat4 viewportMat;
+ matrix::ortho(viewportMat, 0, size.width, size.height, 0, 0, 1);
+
+ const Properties<>::PossiblyEvaluated properties;
+
+ parameters.programs.extrusionTexture.draw(
+ parameters.context, gl::Triangles(), gl::DepthMode::disabled(),
+ gl::StencilMode::disabled(), parameters.colorModeForRenderPass(),
+ ExtrusionTextureProgram::UniformValues{
+ uniforms::u_matrix::Value{ viewportMat }, uniforms::u_world::Value{ size },
+ uniforms::u_image::Value{ 0 },
+ uniforms::u_opacity::Value{ evaluated.get<FillExtrusionOpacity>() } },
+ parameters.staticData.extrusionTextureVertexBuffer,
+ parameters.staticData.quadTriangleIndexBuffer,
+ parameters.staticData.extrusionTextureSegments,
+ ExtrusionTextureProgram::PaintPropertyBinders{ properties, 0 }, properties,
+ parameters.state.getZoom(), getID());
+ }
+}
+
+bool RenderFillExtrusionLayer::queryIntersectsFeature(
+ const GeometryCoordinates& queryGeometry,
+ const GeometryTileFeature& feature,
+ const float,
+ const float bearing,
+ const float pixelsToTileUnits) const {
+
+ auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry(
+ queryGeometry,
+ evaluated.get<style::FillExtrusionTranslate>(),
+ evaluated.get<style::FillExtrusionTranslateAnchor>(),
+ bearing,
+ pixelsToTileUnits);
+
+ return util::polygonIntersectsMultiPolygon(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries());
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp b/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp
new file mode 100644
index 0000000000..838494cf91
--- /dev/null
+++ b/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp
@@ -0,0 +1,44 @@
+#pragma once
+
+#include <mbgl/renderer/render_layer.hpp>
+#include <mbgl/style/layers/fill_extrusion_layer_impl.hpp>
+#include <mbgl/style/layers/fill_extrusion_layer_properties.hpp>
+#include <mbgl/util/optional.hpp>
+#include <mbgl/util/offscreen_texture.hpp>
+
+namespace mbgl {
+
+class RenderFillExtrusionLayer: public RenderLayer {
+public:
+ RenderFillExtrusionLayer(Immutable<style::FillExtrusionLayer::Impl>);
+ ~RenderFillExtrusionLayer() final = default;
+
+ void transition(const TransitionParameters&) override;
+ void evaluate(const PropertyEvaluationParameters&) override;
+ bool hasTransition() const override;
+ void render(PaintParameters&, RenderSource*) override;
+
+ bool queryIntersectsFeature(
+ const GeometryCoordinates&,
+ const GeometryTileFeature&,
+ const float,
+ const float,
+ const float) const override;
+
+ std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override;
+
+ // Paint properties
+ style::FillExtrusionPaintProperties::Unevaluated unevaluated;
+ style::FillExtrusionPaintProperties::PossiblyEvaluated evaluated;
+
+ const style::FillExtrusionLayer::Impl& impl() const;
+
+ optional<OffscreenTexture> renderTexture;
+};
+
+template <>
+inline bool RenderLayer::is<RenderFillExtrusionLayer>() const {
+ return type == style::LayerType::FillExtrusion;
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/layers/render_fill_layer.cpp b/src/mbgl/renderer/layers/render_fill_layer.cpp
new file mode 100644
index 0000000000..22cb9563c1
--- /dev/null
+++ b/src/mbgl/renderer/layers/render_fill_layer.cpp
@@ -0,0 +1,205 @@
+#include <mbgl/renderer/layers/render_fill_layer.hpp>
+#include <mbgl/renderer/buckets/fill_bucket.hpp>
+#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
+#include <mbgl/renderer/image_manager.hpp>
+#include <mbgl/programs/programs.hpp>
+#include <mbgl/programs/fill_program.hpp>
+#include <mbgl/tile/tile.hpp>
+#include <mbgl/style/layers/fill_layer_impl.hpp>
+#include <mbgl/geometry/feature_index.hpp>
+#include <mbgl/util/math.hpp>
+#include <mbgl/util/intersection_tests.hpp>
+
+namespace mbgl {
+
+using namespace style;
+
+RenderFillLayer::RenderFillLayer(Immutable<style::FillLayer::Impl> _impl)
+ : RenderLayer(style::LayerType::Fill, _impl),
+ unevaluated(impl().paint.untransitioned()) {
+}
+
+const style::FillLayer::Impl& RenderFillLayer::impl() const {
+ return static_cast<const style::FillLayer::Impl&>(*baseImpl);
+}
+
+std::unique_ptr<Bucket> RenderFillLayer::createBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) const {
+ return std::make_unique<FillBucket>(parameters, layers);
+}
+
+void RenderFillLayer::transition(const TransitionParameters& parameters) {
+ unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated));
+}
+
+void RenderFillLayer::evaluate(const PropertyEvaluationParameters& parameters) {
+ evaluated = unevaluated.evaluate(parameters);
+
+ if (unevaluated.get<style::FillOutlineColor>().isUndefined()) {
+ evaluated.get<style::FillOutlineColor>() = evaluated.get<style::FillColor>();
+ }
+
+ passes = RenderPass::None;
+
+ if (evaluated.get<style::FillAntialias>()) {
+ passes |= RenderPass::Translucent;
+ }
+
+ if (!unevaluated.get<style::FillPattern>().isUndefined()
+ || evaluated.get<style::FillColor>().constantOr(Color()).a < 1.0f
+ || evaluated.get<style::FillOpacity>().constantOr(0) < 1.0f) {
+ passes |= RenderPass::Translucent;
+ } else {
+ passes |= RenderPass::Opaque;
+ }
+}
+
+bool RenderFillLayer::hasTransition() const {
+ return unevaluated.hasTransition();
+}
+
+void RenderFillLayer::render(PaintParameters& parameters, RenderSource*) {
+ if (evaluated.get<FillPattern>().from.empty()) {
+ for (const RenderTile& tile : renderTiles) {
+ assert(dynamic_cast<FillBucket*>(tile.tile.getBucket(*baseImpl)));
+ FillBucket& bucket = *reinterpret_cast<FillBucket*>(tile.tile.getBucket(*baseImpl));
+
+ auto draw = [&] (auto& program,
+ const auto& drawMode,
+ const auto& depthMode,
+ const auto& indexBuffer,
+ const auto& segments) {
+ program.get(evaluated).draw(
+ parameters.context,
+ drawMode,
+ depthMode,
+ parameters.stencilModeForClipping(tile.clip),
+ parameters.colorModeForRenderPass(),
+ FillProgram::UniformValues {
+ uniforms::u_matrix::Value{
+ tile.translatedMatrix(evaluated.get<FillTranslate>(),
+ evaluated.get<FillTranslateAnchor>(),
+ parameters.state)
+ },
+ uniforms::u_world::Value{ parameters.context.viewport.getCurrentValue().size },
+ },
+ *bucket.vertexBuffer,
+ indexBuffer,
+ segments,
+ bucket.paintPropertyBinders.at(getID()),
+ evaluated,
+ parameters.state.getZoom(),
+ getID()
+ );
+ };
+
+ // Only draw the fill when it's opaque and we're drawing opaque fragments,
+ // or when it's translucent and we're drawing translucent fragments.
+ if ((evaluated.get<FillColor>().constantOr(Color()).a >= 1.0f
+ && evaluated.get<FillOpacity>().constantOr(0) >= 1.0f) == (parameters.pass == RenderPass::Opaque)) {
+ draw(parameters.programs.fill,
+ gl::Triangles(),
+ parameters.depthModeForSublayer(1, parameters.pass == RenderPass::Opaque
+ ? gl::DepthMode::ReadWrite
+ : gl::DepthMode::ReadOnly),
+ *bucket.triangleIndexBuffer,
+ bucket.triangleSegments);
+ }
+
+ if (evaluated.get<FillAntialias>() && parameters.pass == RenderPass::Translucent) {
+ draw(parameters.programs.fillOutline,
+ gl::Lines{ 2.0f },
+ parameters.depthModeForSublayer(
+ unevaluated.get<FillOutlineColor>().isUndefined() ? 2 : 0,
+ gl::DepthMode::ReadOnly),
+ *bucket.lineIndexBuffer,
+ bucket.lineSegments);
+ }
+ }
+ } else {
+ if (parameters.pass != RenderPass::Translucent) {
+ return;
+ }
+
+ optional<ImagePosition> imagePosA = parameters.imageManager.getPattern(evaluated.get<FillPattern>().from);
+ optional<ImagePosition> imagePosB = parameters.imageManager.getPattern(evaluated.get<FillPattern>().to);
+
+ if (!imagePosA || !imagePosB) {
+ return;
+ }
+
+ parameters.imageManager.bind(parameters.context, 0);
+
+ for (const RenderTile& tile : renderTiles) {
+ assert(dynamic_cast<FillBucket*>(tile.tile.getBucket(*baseImpl)));
+ FillBucket& bucket = *reinterpret_cast<FillBucket*>(tile.tile.getBucket(*baseImpl));
+
+ auto draw = [&] (auto& program,
+ const auto& drawMode,
+ const auto& depthMode,
+ const auto& indexBuffer,
+ const auto& segments) {
+ program.get(evaluated).draw(
+ parameters.context,
+ drawMode,
+ depthMode,
+ parameters.stencilModeForClipping(tile.clip),
+ parameters.colorModeForRenderPass(),
+ FillPatternUniforms::values(
+ tile.translatedMatrix(evaluated.get<FillTranslate>(),
+ evaluated.get<FillTranslateAnchor>(),
+ parameters.state),
+ parameters.context.viewport.getCurrentValue().size,
+ parameters.imageManager.getPixelSize(),
+ *imagePosA,
+ *imagePosB,
+ evaluated.get<FillPattern>(),
+ tile.id,
+ parameters.state
+ ),
+ *bucket.vertexBuffer,
+ indexBuffer,
+ segments,
+ bucket.paintPropertyBinders.at(getID()),
+ evaluated,
+ parameters.state.getZoom(),
+ getID()
+ );
+ };
+
+ draw(parameters.programs.fillPattern,
+ gl::Triangles(),
+ parameters.depthModeForSublayer(1, gl::DepthMode::ReadWrite),
+ *bucket.triangleIndexBuffer,
+ bucket.triangleSegments);
+
+ if (evaluated.get<FillAntialias>() && unevaluated.get<FillOutlineColor>().isUndefined()) {
+ draw(parameters.programs.fillOutlinePattern,
+ gl::Lines { 2.0f },
+ parameters.depthModeForSublayer(2, gl::DepthMode::ReadOnly),
+ *bucket.lineIndexBuffer,
+ bucket.lineSegments);
+ }
+ }
+ }
+}
+
+bool RenderFillLayer::queryIntersectsFeature(
+ const GeometryCoordinates& queryGeometry,
+ const GeometryTileFeature& feature,
+ const float,
+ const float bearing,
+ const float pixelsToTileUnits) const {
+
+ auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry(
+ queryGeometry,
+ evaluated.get<style::FillTranslate>(),
+ evaluated.get<style::FillTranslateAnchor>(),
+ bearing,
+ pixelsToTileUnits);
+
+ return util::polygonIntersectsMultiPolygon(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries());
+}
+
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/layers/render_fill_layer.hpp b/src/mbgl/renderer/layers/render_fill_layer.hpp
new file mode 100644
index 0000000000..a51865698f
--- /dev/null
+++ b/src/mbgl/renderer/layers/render_fill_layer.hpp
@@ -0,0 +1,40 @@
+#pragma once
+
+#include <mbgl/renderer/render_layer.hpp>
+#include <mbgl/style/layers/fill_layer_impl.hpp>
+#include <mbgl/style/layers/fill_layer_properties.hpp>
+
+namespace mbgl {
+
+class RenderFillLayer: public RenderLayer {
+public:
+ RenderFillLayer(Immutable<style::FillLayer::Impl>);
+ ~RenderFillLayer() final = default;
+
+ void transition(const TransitionParameters&) override;
+ void evaluate(const PropertyEvaluationParameters&) override;
+ bool hasTransition() const override;
+ void render(PaintParameters&, RenderSource*) override;
+
+ bool queryIntersectsFeature(
+ const GeometryCoordinates&,
+ const GeometryTileFeature&,
+ const float,
+ const float,
+ const float) const override;
+
+ std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override;
+
+ // Paint properties
+ style::FillPaintProperties::Unevaluated unevaluated;
+ style::FillPaintProperties::PossiblyEvaluated evaluated;
+
+ const style::FillLayer::Impl& impl() const;
+};
+
+template <>
+inline bool RenderLayer::is<RenderFillLayer>() const {
+ return type == style::LayerType::Fill;
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/layers/render_line_layer.cpp b/src/mbgl/renderer/layers/render_line_layer.cpp
new file mode 100644
index 0000000000..1b4a1c0ff7
--- /dev/null
+++ b/src/mbgl/renderer/layers/render_line_layer.cpp
@@ -0,0 +1,204 @@
+#include <mbgl/renderer/layers/render_line_layer.hpp>
+#include <mbgl/renderer/buckets/line_bucket.hpp>
+#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
+#include <mbgl/renderer/image_manager.hpp>
+#include <mbgl/programs/programs.hpp>
+#include <mbgl/programs/line_program.hpp>
+#include <mbgl/geometry/line_atlas.hpp>
+#include <mbgl/tile/tile.hpp>
+#include <mbgl/style/layers/line_layer_impl.hpp>
+#include <mbgl/geometry/feature_index.hpp>
+#include <mbgl/util/math.hpp>
+#include <mbgl/util/intersection_tests.hpp>
+
+namespace mbgl {
+
+using namespace style;
+
+RenderLineLayer::RenderLineLayer(Immutable<style::LineLayer::Impl> _impl)
+ : RenderLayer(style::LayerType::Line, _impl),
+ unevaluated(impl().paint.untransitioned()) {
+}
+
+const style::LineLayer::Impl& RenderLineLayer::impl() const {
+ return static_cast<const style::LineLayer::Impl&>(*baseImpl);
+}
+
+std::unique_ptr<Bucket> RenderLineLayer::createBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) const {
+ return std::make_unique<LineBucket>(parameters, layers, impl().layout);
+}
+
+void RenderLineLayer::transition(const TransitionParameters& parameters) {
+ unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated));
+}
+
+void RenderLineLayer::evaluate(const PropertyEvaluationParameters& parameters) {
+ style::Properties<LineFloorwidth>::Unevaluated extra(unevaluated.get<style::LineWidth>());
+
+ auto dashArrayParams = parameters;
+ dashArrayParams.useIntegerZoom = true;
+
+ evaluated = RenderLinePaintProperties::PossiblyEvaluated(
+ unevaluated.evaluate(parameters).concat(extra.evaluate(dashArrayParams)));
+
+ passes = (evaluated.get<style::LineOpacity>().constantOr(1.0) > 0
+ && evaluated.get<style::LineColor>().constantOr(Color::black()).a > 0
+ && evaluated.get<style::LineWidth>().constantOr(1.0) > 0)
+ ? RenderPass::Translucent : RenderPass::None;
+}
+
+bool RenderLineLayer::hasTransition() const {
+ return unevaluated.hasTransition();
+}
+
+void RenderLineLayer::render(PaintParameters& parameters, RenderSource*) {
+ if (parameters.pass == RenderPass::Opaque) {
+ return;
+ }
+
+ for (const RenderTile& tile : renderTiles) {
+ assert(dynamic_cast<LineBucket*>(tile.tile.getBucket(*baseImpl)));
+ LineBucket& bucket = *reinterpret_cast<LineBucket*>(tile.tile.getBucket(*baseImpl));
+
+ auto draw = [&] (auto& program, auto&& uniformValues) {
+ program.get(evaluated).draw(
+ parameters.context,
+ gl::Triangles(),
+ parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly),
+ parameters.stencilModeForClipping(tile.clip),
+ parameters.colorModeForRenderPass(),
+ std::move(uniformValues),
+ *bucket.vertexBuffer,
+ *bucket.indexBuffer,
+ bucket.segments,
+ bucket.paintPropertyBinders.at(getID()),
+ evaluated,
+ parameters.state.getZoom(),
+ getID()
+ );
+ };
+
+ if (!evaluated.get<LineDasharray>().from.empty()) {
+ const LinePatternCap cap = bucket.layout.get<LineCap>() == LineCapType::Round
+ ? LinePatternCap::Round : LinePatternCap::Square;
+ LinePatternPos posA = parameters.lineAtlas.getDashPosition(evaluated.get<LineDasharray>().from, cap);
+ LinePatternPos posB = parameters.lineAtlas.getDashPosition(evaluated.get<LineDasharray>().to, cap);
+
+ parameters.lineAtlas.bind(parameters.context, 0);
+
+ draw(parameters.programs.lineSDF,
+ LineSDFProgram::uniformValues(
+ evaluated,
+ parameters.pixelRatio,
+ tile,
+ parameters.state,
+ parameters.pixelsToGLUnits,
+ posA,
+ posB,
+ parameters.lineAtlas.getSize().width));
+
+ } else if (!evaluated.get<LinePattern>().from.empty()) {
+ optional<ImagePosition> posA = parameters.imageManager.getPattern(evaluated.get<LinePattern>().from);
+ optional<ImagePosition> posB = parameters.imageManager.getPattern(evaluated.get<LinePattern>().to);
+
+ if (!posA || !posB)
+ return;
+
+ parameters.imageManager.bind(parameters.context, 0);
+
+ draw(parameters.programs.linePattern,
+ LinePatternProgram::uniformValues(
+ evaluated,
+ tile,
+ parameters.state,
+ parameters.pixelsToGLUnits,
+ parameters.imageManager.getPixelSize(),
+ *posA,
+ *posB));
+
+ } else {
+ draw(parameters.programs.line,
+ LineProgram::uniformValues(
+ evaluated,
+ tile,
+ parameters.state,
+ parameters.pixelsToGLUnits));
+ }
+ }
+}
+
+optional<GeometryCollection> offsetLine(const GeometryCollection& rings, const double offset) {
+ if (offset == 0) return {};
+
+ GeometryCollection newRings;
+ Point<double> zero(0, 0);
+ for (const auto& ring : rings) {
+ newRings.emplace_back();
+ auto& newRing = newRings.back();
+
+ for (auto i = ring.begin(); i != ring.end(); i++) {
+ auto& p = *i;
+
+ Point<double> aToB = i == ring.begin() ?
+ zero :
+ util::perp(util::unit(convertPoint<double>(p - *(i - 1))));
+ Point<double> bToC = i + 1 == ring.end() ?
+ zero :
+ util::perp(util::unit(convertPoint<double>(*(i + 1) - p)));
+ Point<double> extrude = util::unit(aToB + bToC);
+
+ const double cosHalfAngle = extrude.x * bToC.x + extrude.y * bToC.y;
+ extrude *= (1.0 / cosHalfAngle);
+
+ newRing.push_back(convertPoint<int16_t>(extrude * offset) + p);
+ }
+ }
+
+ return newRings;
+}
+
+bool RenderLineLayer::queryIntersectsFeature(
+ const GeometryCoordinates& queryGeometry,
+ const GeometryTileFeature& feature,
+ const float zoom,
+ const float bearing,
+ const float pixelsToTileUnits) const {
+
+ // Translate query geometry
+ auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry(
+ queryGeometry,
+ evaluated.get<style::LineTranslate>(),
+ evaluated.get<style::LineTranslateAnchor>(),
+ bearing,
+ pixelsToTileUnits);
+
+ // Evaluate function
+ auto offset = evaluated.get<style::LineOffset>()
+ .evaluate(feature, zoom, style::LineOffset::defaultValue()) * pixelsToTileUnits;
+
+ // Apply offset to geometry
+ auto offsetGeometry = offsetLine(feature.getGeometries(), offset);
+
+ // Test intersection
+ const float halfWidth = getLineWidth(feature, zoom) / 2.0 * pixelsToTileUnits;
+ return util::polygonIntersectsBufferedMultiLine(
+ translatedQueryGeometry.value_or(queryGeometry),
+ offsetGeometry.value_or(feature.getGeometries()),
+ halfWidth);
+}
+
+float RenderLineLayer::getLineWidth(const GeometryTileFeature& feature, const float zoom) const {
+ float lineWidth = evaluated.get<style::LineWidth>()
+ .evaluate(feature, zoom, style::LineWidth::defaultValue());
+ float gapWidth = evaluated.get<style::LineGapWidth>()
+ .evaluate(feature, zoom, style::LineGapWidth::defaultValue());
+ if (gapWidth) {
+ return gapWidth + 2 * lineWidth;
+ } else {
+ return lineWidth;
+ }
+}
+
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/layers/render_line_layer.hpp b/src/mbgl/renderer/layers/render_line_layer.hpp
new file mode 100644
index 0000000000..8bf7e2329d
--- /dev/null
+++ b/src/mbgl/renderer/layers/render_line_layer.hpp
@@ -0,0 +1,52 @@
+#pragma once
+
+#include <mbgl/renderer/render_layer.hpp>
+#include <mbgl/style/layers/line_layer_impl.hpp>
+#include <mbgl/style/layers/line_layer_properties.hpp>
+#include <mbgl/programs/uniforms.hpp>
+
+namespace mbgl {
+
+struct LineFloorwidth : style::DataDrivenPaintProperty<float, attributes::a_floorwidth, uniforms::u_floorwidth> {
+ static float defaultValue() { return 1; }
+};
+
+class RenderLinePaintProperties : public style::ConcatenateProperties<
+ style::LinePaintProperties::PropertyTypes,
+ TypeList<LineFloorwidth>>::Type {};
+
+class RenderLineLayer: public RenderLayer {
+public:
+ RenderLineLayer(Immutable<style::LineLayer::Impl>);
+ ~RenderLineLayer() final = default;
+
+ void transition(const TransitionParameters&) override;
+ void evaluate(const PropertyEvaluationParameters&) override;
+ bool hasTransition() const override;
+ void render(PaintParameters&, RenderSource*) override;
+
+ bool queryIntersectsFeature(
+ const GeometryCoordinates&,
+ const GeometryTileFeature&,
+ const float,
+ const float,
+ const float) const override;
+
+ std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override;
+
+ // Paint properties
+ style::LinePaintProperties::Unevaluated unevaluated;
+ RenderLinePaintProperties::PossiblyEvaluated evaluated;
+
+ const style::LineLayer::Impl& impl() const;
+
+private:
+ float getLineWidth(const GeometryTileFeature&, const float) const;
+};
+
+template <>
+inline bool RenderLayer::is<RenderLineLayer>() const {
+ return type == style::LayerType::Line;
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/layers/render_raster_layer.cpp b/src/mbgl/renderer/layers/render_raster_layer.cpp
new file mode 100644
index 0000000000..06616d90e5
--- /dev/null
+++ b/src/mbgl/renderer/layers/render_raster_layer.cpp
@@ -0,0 +1,155 @@
+#include <mbgl/renderer/layers/render_raster_layer.hpp>
+#include <mbgl/renderer/buckets/raster_bucket.hpp>
+#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
+#include <mbgl/renderer/sources/render_image_source.hpp>
+#include <mbgl/renderer/render_static_data.hpp>
+#include <mbgl/programs/programs.hpp>
+#include <mbgl/programs/raster_program.hpp>
+#include <mbgl/tile/tile.hpp>
+#include <mbgl/style/layers/raster_layer_impl.hpp>
+
+namespace mbgl {
+
+using namespace style;
+
+RenderRasterLayer::RenderRasterLayer(Immutable<style::RasterLayer::Impl> _impl)
+ : RenderLayer(style::LayerType::Raster, _impl),
+ unevaluated(impl().paint.untransitioned()) {
+}
+
+const style::RasterLayer::Impl& RenderRasterLayer::impl() const {
+ return static_cast<const style::RasterLayer::Impl&>(*baseImpl);
+}
+
+std::unique_ptr<Bucket> RenderRasterLayer::createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const {
+ assert(false);
+ return nullptr;
+}
+
+void RenderRasterLayer::transition(const TransitionParameters& parameters) {
+ unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated));
+}
+
+void RenderRasterLayer::evaluate(const PropertyEvaluationParameters& parameters) {
+ evaluated = unevaluated.evaluate(parameters);
+
+ passes = evaluated.get<style::RasterOpacity>() > 0 ? RenderPass::Translucent : RenderPass::None;
+}
+
+bool RenderRasterLayer::hasTransition() const {
+ return unevaluated.hasTransition();
+}
+
+static float saturationFactor(float saturation) {
+ if (saturation > 0) {
+ return 1 - 1 / (1.001 - saturation);
+ } else {
+ return -saturation;
+ }
+}
+
+static float contrastFactor(float contrast) {
+ if (contrast > 0) {
+ return 1 / (1 - contrast);
+ } else {
+ return 1 + contrast;
+ }
+}
+
+static std::array<float, 3> spinWeights(float spin) {
+ spin *= util::DEG2RAD;
+ float s = std::sin(spin);
+ float c = std::cos(spin);
+ std::array<float, 3> spin_weights = {{
+ (2 * c + 1) / 3,
+ (-std::sqrt(3.0f) * s - c + 1) / 3,
+ (std::sqrt(3.0f) * s - c + 1) / 3
+ }};
+ return spin_weights;
+}
+
+void RenderRasterLayer::render(PaintParameters& parameters, RenderSource* source) {
+ if (parameters.pass != RenderPass::Translucent)
+ return;
+
+ auto draw = [&] (const mat4& matrix,
+ const auto& vertexBuffer,
+ const auto& indexBuffer,
+ const auto& segments) {
+ parameters.programs.raster.draw(
+ parameters.context,
+ gl::Triangles(),
+ parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly),
+ gl::StencilMode::disabled(),
+ parameters.colorModeForRenderPass(),
+ RasterProgram::UniformValues {
+ uniforms::u_matrix::Value{ matrix },
+ uniforms::u_image0::Value{ 0 },
+ uniforms::u_image1::Value{ 1 },
+ uniforms::u_opacity::Value{ evaluated.get<RasterOpacity>() },
+ uniforms::u_fade_t::Value{ 1 },
+ uniforms::u_brightness_low::Value{ evaluated.get<RasterBrightnessMin>() },
+ uniforms::u_brightness_high::Value{ evaluated.get<RasterBrightnessMax>() },
+ uniforms::u_saturation_factor::Value{ saturationFactor(evaluated.get<RasterSaturation>()) },
+ uniforms::u_contrast_factor::Value{ contrastFactor(evaluated.get<RasterContrast>()) },
+ uniforms::u_spin_weights::Value{ spinWeights(evaluated.get<RasterHueRotate>()) },
+ uniforms::u_buffer_scale::Value{ 1.0f },
+ uniforms::u_scale_parent::Value{ 1.0f },
+ uniforms::u_tl_parent::Value{ std::array<float, 2> {{ 0.0f, 0.0f }} },
+ },
+ vertexBuffer,
+ indexBuffer,
+ segments,
+ RasterProgram::PaintPropertyBinders { evaluated, 0 },
+ evaluated,
+ parameters.state.getZoom(),
+ getID()
+ );
+ };
+
+ if (RenderImageSource* imageSource = source->as<RenderImageSource>()) {
+ if (imageSource->isEnabled() && imageSource->isLoaded() && !imageSource->bucket->needsUpload()) {
+ RasterBucket& bucket = *imageSource->bucket;
+
+ assert(bucket.texture);
+ parameters.context.bindTexture(*bucket.texture, 0, gl::TextureFilter::Linear);
+ parameters.context.bindTexture(*bucket.texture, 1, gl::TextureFilter::Linear);
+
+ for (auto matrix_ : imageSource->matrices) {
+ draw(matrix_,
+ *bucket.vertexBuffer,
+ *bucket.indexBuffer,
+ bucket.segments);
+ }
+ }
+ } else {
+ for (const RenderTile& tile : renderTiles) {
+ assert(dynamic_cast<RasterBucket*>(tile.tile.getBucket(*baseImpl)));
+ RasterBucket& bucket = *reinterpret_cast<RasterBucket*>(tile.tile.getBucket(*baseImpl));
+
+ if (!bucket.hasData())
+ continue;
+
+ assert(bucket.texture);
+ parameters.context.bindTexture(*bucket.texture, 0, gl::TextureFilter::Linear);
+ parameters.context.bindTexture(*bucket.texture, 1, gl::TextureFilter::Linear);
+
+ if (bucket.vertexBuffer && bucket.indexBuffer && !bucket.segments.empty()) {
+ // Draw only the parts of the tile that aren't drawn by another tile in the layer.
+ draw(tile.matrix,
+ *bucket.vertexBuffer,
+ *bucket.indexBuffer,
+ bucket.segments);
+ } else {
+ // Draw the full tile.
+ draw(tile.matrix,
+ parameters.staticData.rasterVertexBuffer,
+ parameters.staticData.quadTriangleIndexBuffer,
+ parameters.staticData.rasterSegments);
+ }
+ }
+ }
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/layers/render_raster_layer.hpp b/src/mbgl/renderer/layers/render_raster_layer.hpp
new file mode 100644
index 0000000000..87de316f7c
--- /dev/null
+++ b/src/mbgl/renderer/layers/render_raster_layer.hpp
@@ -0,0 +1,34 @@
+#pragma once
+
+#include <mbgl/renderer/render_layer.hpp>
+#include <mbgl/style/layers/raster_layer_impl.hpp>
+#include <mbgl/style/layers/raster_layer_properties.hpp>
+
+namespace mbgl {
+
+class RenderRasterLayer: public RenderLayer {
+public:
+ RenderRasterLayer(Immutable<style::RasterLayer::Impl>);
+ ~RenderRasterLayer() final = default;
+
+ void transition(const TransitionParameters&) override;
+ void evaluate(const PropertyEvaluationParameters&) override;
+ bool hasTransition() const override;
+
+ void render(PaintParameters&, RenderSource*) override;
+
+ std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override;
+
+ // Paint properties
+ style::RasterPaintProperties::Unevaluated unevaluated;
+ style::RasterPaintProperties::PossiblyEvaluated evaluated;
+
+ const style::RasterLayer::Impl& impl() const;
+};
+
+template <>
+inline bool RenderLayer::is<RenderRasterLayer>() const {
+ return type == style::LayerType::Raster;
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/layers/render_symbol_layer.cpp b/src/mbgl/renderer/layers/render_symbol_layer.cpp
new file mode 100644
index 0000000000..1376e8a3d8
--- /dev/null
+++ b/src/mbgl/renderer/layers/render_symbol_layer.cpp
@@ -0,0 +1,325 @@
+#include <mbgl/renderer/layers/render_symbol_layer.hpp>
+#include <mbgl/renderer/buckets/symbol_bucket.hpp>
+#include <mbgl/renderer/bucket_parameters.hpp>
+#include <mbgl/renderer/property_evaluation_parameters.hpp>
+#include <mbgl/renderer/render_tile.hpp>
+#include <mbgl/renderer/paint_parameters.hpp>
+#include <mbgl/renderer/frame_history.hpp>
+#include <mbgl/text/glyph_atlas.hpp>
+#include <mbgl/programs/programs.hpp>
+#include <mbgl/programs/symbol_program.hpp>
+#include <mbgl/programs/collision_box_program.hpp>
+#include <mbgl/tile/tile.hpp>
+#include <mbgl/tile/geometry_tile.hpp>
+#include <mbgl/tile/geometry_tile_data.hpp>
+#include <mbgl/style/layers/symbol_layer_impl.hpp>
+#include <mbgl/layout/symbol_layout.hpp>
+#include <mbgl/layout/symbol_projection.hpp>
+#include <mbgl/util/math.hpp>
+
+#include <cmath>
+
+namespace mbgl {
+
+using namespace style;
+
+RenderSymbolLayer::RenderSymbolLayer(Immutable<style::SymbolLayer::Impl> _impl)
+ : RenderLayer(style::LayerType::Symbol, _impl),
+ unevaluated(impl().paint.untransitioned()) {
+}
+
+const style::SymbolLayer::Impl& RenderSymbolLayer::impl() const {
+ return static_cast<const style::SymbolLayer::Impl&>(*baseImpl);
+}
+
+std::unique_ptr<Bucket> RenderSymbolLayer::createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const {
+ assert(false); // Should be calling createLayout() instead.
+ return nullptr;
+}
+
+std::unique_ptr<SymbolLayout> RenderSymbolLayer::createLayout(const BucketParameters& parameters,
+ const std::vector<const RenderLayer*>& group,
+ std::unique_ptr<GeometryTileLayer> layer,
+ GlyphDependencies& glyphDependencies,
+ ImageDependencies& imageDependencies) const {
+ return std::make_unique<SymbolLayout>(parameters,
+ group,
+ std::move(layer),
+ imageDependencies,
+ glyphDependencies);
+}
+
+void RenderSymbolLayer::transition(const TransitionParameters& parameters) {
+ unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated));
+}
+
+void RenderSymbolLayer::evaluate(const PropertyEvaluationParameters& parameters) {
+ evaluated = unevaluated.evaluate(parameters);
+
+ auto hasIconOpacity = evaluated.get<style::IconColor>().constantOr(Color::black()).a > 0 ||
+ evaluated.get<style::IconHaloColor>().constantOr(Color::black()).a > 0;
+ auto hasTextOpacity = evaluated.get<style::TextColor>().constantOr(Color::black()).a > 0 ||
+ evaluated.get<style::TextHaloColor>().constantOr(Color::black()).a > 0;
+
+ passes = ((evaluated.get<style::IconOpacity>().constantOr(1) > 0 && hasIconOpacity && iconSize > 0)
+ || (evaluated.get<style::TextOpacity>().constantOr(1) > 0 && hasTextOpacity && textSize > 0))
+ ? RenderPass::Translucent : RenderPass::None;
+}
+
+bool RenderSymbolLayer::hasTransition() const {
+ return unevaluated.hasTransition();
+}
+
+void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) {
+ if (parameters.pass == RenderPass::Opaque) {
+ return;
+ }
+
+ for (const RenderTile& tile : renderTiles) {
+ assert(dynamic_cast<SymbolBucket*>(tile.tile.getBucket(*baseImpl)));
+ SymbolBucket& bucket = *reinterpret_cast<SymbolBucket*>(tile.tile.getBucket(*baseImpl));
+
+ const auto& layout = bucket.layout;
+
+ parameters.frameHistory.bind(parameters.context, 1);
+
+ auto draw = [&] (auto& program,
+ auto&& uniformValues,
+ const auto& buffers,
+ const auto& symbolSizeBinder,
+ const SymbolPropertyValues& values_,
+ const auto& binders,
+ const auto& paintProperties)
+ {
+ // We clip symbols to their tile extent in still mode.
+ const bool needsClipping = parameters.mapMode == MapMode::Still;
+
+ program.get(paintProperties).draw(
+ parameters.context,
+ gl::Triangles(),
+ values_.pitchAlignment == AlignmentType::Map
+ ? parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly)
+ : gl::DepthMode::disabled(),
+ needsClipping
+ ? parameters.stencilModeForClipping(tile.clip)
+ : gl::StencilMode::disabled(),
+ parameters.colorModeForRenderPass(),
+ std::move(uniformValues),
+ *buffers.vertexBuffer,
+ *buffers.dynamicVertexBuffer,
+ *symbolSizeBinder,
+ *buffers.indexBuffer,
+ buffers.segments,
+ binders,
+ paintProperties,
+ parameters.state.getZoom(),
+ getID()
+ );
+ };
+
+ assert(dynamic_cast<GeometryTile*>(&tile.tile));
+ GeometryTile& geometryTile = static_cast<GeometryTile&>(tile.tile);
+
+ if (bucket.hasIconData()) {
+ auto values = iconPropertyValues(layout);
+ auto paintPropertyValues = iconPaintProperties();
+
+ const bool alongLine = layout.get<SymbolPlacement>() == SymbolPlacementType::Line &&
+ layout.get<IconRotationAlignment>() == AlignmentType::Map;
+
+ if (alongLine) {
+ reprojectLineLabels(bucket.icon.dynamicVertices,
+ bucket.icon.placedSymbols,
+ tile.matrix,
+ values,
+ tile,
+ *bucket.iconSizeBinder,
+ parameters.state,
+ parameters.frameHistory);
+
+ parameters.context.updateVertexBuffer(*bucket.icon.dynamicVertexBuffer, std::move(bucket.icon.dynamicVertices));
+ }
+
+ const bool iconScaled = layout.get<IconSize>().constantOr(1.0) != 1.0 || bucket.iconsNeedLinear;
+ const bool iconTransformed = values.rotationAlignment == AlignmentType::Map || parameters.state.getPitch() != 0;
+
+ parameters.context.bindTexture(*geometryTile.iconAtlasTexture, 0,
+ bucket.sdfIcons || parameters.state.isChanging() || iconScaled || iconTransformed
+ ? gl::TextureFilter::Linear : gl::TextureFilter::Nearest);
+
+ const Size texsize = geometryTile.iconAtlasTexture->size;
+
+ if (bucket.sdfIcons) {
+ if (values.hasHalo) {
+ draw(parameters.programs.symbolIconSDF,
+ SymbolSDFIconProgram::uniformValues(false, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, SymbolSDFPart::Halo),
+ bucket.icon,
+ bucket.iconSizeBinder,
+ values,
+ bucket.paintPropertyBinders.at(getID()).first,
+ paintPropertyValues);
+ }
+
+ if (values.hasFill) {
+ draw(parameters.programs.symbolIconSDF,
+ SymbolSDFIconProgram::uniformValues(false, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, SymbolSDFPart::Fill),
+ bucket.icon,
+ bucket.iconSizeBinder,
+ values,
+ bucket.paintPropertyBinders.at(getID()).first,
+ paintPropertyValues);
+ }
+ } else {
+ draw(parameters.programs.symbolIcon,
+ SymbolIconProgram::uniformValues(false, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state),
+ bucket.icon,
+ bucket.iconSizeBinder,
+ values,
+ bucket.paintPropertyBinders.at(getID()).first,
+ paintPropertyValues);
+ }
+ }
+
+ if (bucket.hasTextData()) {
+ parameters.context.bindTexture(*geometryTile.glyphAtlasTexture, 0, gl::TextureFilter::Linear);
+
+ auto values = textPropertyValues(layout);
+ auto paintPropertyValues = textPaintProperties();
+
+ const bool alongLine = layout.get<SymbolPlacement>() == SymbolPlacementType::Line &&
+ layout.get<TextRotationAlignment>() == AlignmentType::Map;
+
+ if (alongLine) {
+ reprojectLineLabels(bucket.text.dynamicVertices,
+ bucket.text.placedSymbols,
+ tile.matrix,
+ values,
+ tile,
+ *bucket.textSizeBinder,
+ parameters.state,
+ parameters.frameHistory);
+
+ parameters.context.updateVertexBuffer(*bucket.text.dynamicVertexBuffer, std::move(bucket.text.dynamicVertices));
+ }
+
+ const Size texsize = geometryTile.glyphAtlasTexture->size;
+
+ if (values.hasHalo) {
+ draw(parameters.programs.symbolGlyph,
+ SymbolSDFTextProgram::uniformValues(true, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, SymbolSDFPart::Halo),
+ bucket.text,
+ bucket.textSizeBinder,
+ values,
+ bucket.paintPropertyBinders.at(getID()).second,
+ paintPropertyValues);
+ }
+
+ if (values.hasFill) {
+ draw(parameters.programs.symbolGlyph,
+ SymbolSDFTextProgram::uniformValues(true, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, SymbolSDFPart::Fill),
+ bucket.text,
+ bucket.textSizeBinder,
+ values,
+ bucket.paintPropertyBinders.at(getID()).second,
+ paintPropertyValues);
+ }
+ }
+
+ if (bucket.hasCollisionBoxData()) {
+ static const style::Properties<>::PossiblyEvaluated properties {};
+ static const CollisionBoxProgram::PaintPropertyBinders paintAttributeData(properties, 0);
+
+ parameters.programs.collisionBox.draw(
+ parameters.context,
+ gl::Lines { 1.0f },
+ gl::DepthMode::disabled(),
+ parameters.stencilModeForClipping(tile.clip),
+ parameters.colorModeForRenderPass(),
+ CollisionBoxProgram::UniformValues {
+ uniforms::u_matrix::Value{ tile.matrix },
+ uniforms::u_scale::Value{ std::pow(2.0f, float(parameters.state.getZoom() - tile.tile.id.overscaledZ)) },
+ uniforms::u_zoom::Value{ float(parameters.state.getZoom() * 10) },
+ uniforms::u_maxzoom::Value{ float((tile.id.canonical.z + 1) * 10) },
+ uniforms::u_collision_y_stretch::Value{ tile.tile.yStretch() },
+ uniforms::u_camera_to_center_distance::Value{ parameters.state.getCameraToCenterDistance() },
+ uniforms::u_pitch::Value{ parameters.state.getPitch() },
+ uniforms::u_fadetexture::Value{ 1 }
+ },
+ *bucket.collisionBox.vertexBuffer,
+ *bucket.collisionBox.indexBuffer,
+ bucket.collisionBox.segments,
+ paintAttributeData,
+ properties,
+ parameters.state.getZoom(),
+ getID()
+ );
+ }
+ }
+}
+
+style::IconPaintProperties::PossiblyEvaluated RenderSymbolLayer::iconPaintProperties() const {
+ return style::IconPaintProperties::PossiblyEvaluated {
+ evaluated.get<style::IconOpacity>(),
+ evaluated.get<style::IconColor>(),
+ evaluated.get<style::IconHaloColor>(),
+ evaluated.get<style::IconHaloWidth>(),
+ evaluated.get<style::IconHaloBlur>(),
+ evaluated.get<style::IconTranslate>(),
+ evaluated.get<style::IconTranslateAnchor>()
+ };
+}
+
+style::TextPaintProperties::PossiblyEvaluated RenderSymbolLayer::textPaintProperties() const {
+ return style::TextPaintProperties::PossiblyEvaluated {
+ evaluated.get<style::TextOpacity>(),
+ evaluated.get<style::TextColor>(),
+ evaluated.get<style::TextHaloColor>(),
+ evaluated.get<style::TextHaloWidth>(),
+ evaluated.get<style::TextHaloBlur>(),
+ evaluated.get<style::TextTranslate>(),
+ evaluated.get<style::TextTranslateAnchor>()
+ };
+}
+
+
+style::SymbolPropertyValues RenderSymbolLayer::iconPropertyValues(const style::SymbolLayoutProperties::PossiblyEvaluated& layout_) const {
+ return style::SymbolPropertyValues {
+ layout_.get<style::IconPitchAlignment>(),
+ layout_.get<style::IconRotationAlignment>(),
+ layout_.get<style::IconKeepUpright>(),
+ evaluated.get<style::IconTranslate>(),
+ evaluated.get<style::IconTranslateAnchor>(),
+ evaluated.get<style::IconHaloColor>().constantOr(Color::black()).a > 0 &&
+ evaluated.get<style::IconHaloWidth>().constantOr(1),
+ evaluated.get<style::IconColor>().constantOr(Color::black()).a > 0,
+ 10.0f
+ };
+}
+
+style::SymbolPropertyValues RenderSymbolLayer::textPropertyValues(const style::SymbolLayoutProperties::PossiblyEvaluated& layout_) const {
+ // We hide line labels with viewport alignment as they move into the distance
+ // because the approximations we use for drawing their glyphs get progressively worse
+ // The "1.5" here means we start hiding them when the distance from the label
+ // to the camera is 50% greater than the distance from the center of the map
+ // to the camera. Depending on viewport properties, you might expect this to filter
+ // the top third of the screen at pitch 60, and do almost nothing at pitch 45
+ // "10" is effectively infinite at any pitch we support
+ const bool limitMaxDistance =
+ layout_.get<style::SymbolPlacement>() == style::SymbolPlacementType::Line
+ && layout_.get<style::TextRotationAlignment>() == style::AlignmentType::Map
+ && layout_.get<style::TextPitchAlignment>() == style::AlignmentType::Viewport;
+
+ return style::SymbolPropertyValues {
+ layout_.get<style::TextPitchAlignment>(),
+ layout_.get<style::TextRotationAlignment>(),
+ layout_.get<style::TextKeepUpright>(),
+ evaluated.get<style::TextTranslate>(),
+ evaluated.get<style::TextTranslateAnchor>(),
+ evaluated.get<style::TextHaloColor>().constantOr(Color::black()).a > 0 &&
+ evaluated.get<style::TextHaloWidth>().constantOr(1),
+ evaluated.get<style::TextColor>().constantOr(Color::black()).a > 0,
+ limitMaxDistance ? 1.5f : 10.0f
+ };
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/renderer/layers/render_symbol_layer.hpp b/src/mbgl/renderer/layers/render_symbol_layer.hpp
new file mode 100644
index 0000000000..83709b5122
--- /dev/null
+++ b/src/mbgl/renderer/layers/render_symbol_layer.hpp
@@ -0,0 +1,99 @@
+#pragma once
+
+#include <mbgl/text/glyph.hpp>
+#include <mbgl/renderer/render_layer.hpp>
+#include <mbgl/style/image_impl.hpp>
+#include <mbgl/style/layers/symbol_layer_impl.hpp>
+#include <mbgl/style/layers/symbol_layer_properties.hpp>
+
+namespace mbgl {
+
+namespace style {
+
+// {icon,text}-specific paint-property packs for use in the symbol Programs.
+// Since each program deals either with icons or text, using a smaller property set
+// lets us avoid unnecessarily binding attributes for properties the program wouldn't use.
+class IconPaintProperties : public Properties<
+ IconOpacity,
+ IconColor,
+ IconHaloColor,
+ IconHaloWidth,
+ IconHaloBlur,
+ IconTranslate,
+ IconTranslateAnchor
+> {};
+
+class TextPaintProperties : public Properties<
+ TextOpacity,
+ TextColor,
+ TextHaloColor,
+ TextHaloWidth,
+ TextHaloBlur,
+ TextTranslate,
+ TextTranslateAnchor
+> {};
+
+// Repackaging evaluated values from SymbolLayoutProperties + SymbolPaintProperties
+// for genericity over icons vs. text.
+class SymbolPropertyValues {
+public:
+ // Layout
+ AlignmentType pitchAlignment;
+ AlignmentType rotationAlignment;
+ bool keepUpright;
+
+ // Paint
+ std::array<float, 2> translate;
+ TranslateAnchorType translateAnchor;
+
+ bool hasHalo;
+ bool hasFill;
+
+ float maxCameraDistance; // 1.5 for road labels, or 10 (essentially infinite) for everything else
+};
+
+} // namespace style
+
+class BucketParameters;
+class SymbolLayout;
+class GeometryTileLayer;
+
+class RenderSymbolLayer: public RenderLayer {
+public:
+ RenderSymbolLayer(Immutable<style::SymbolLayer::Impl>);
+ ~RenderSymbolLayer() final = default;
+
+ void transition(const TransitionParameters&) override;
+ void evaluate(const PropertyEvaluationParameters&) override;
+ bool hasTransition() const override;
+ void render(PaintParameters&, RenderSource*) override;
+
+ style::IconPaintProperties::PossiblyEvaluated iconPaintProperties() const;
+ style::TextPaintProperties::PossiblyEvaluated textPaintProperties() const;
+
+ style::SymbolPropertyValues iconPropertyValues(const style::SymbolLayoutProperties::PossiblyEvaluated&) const;
+ style::SymbolPropertyValues textPropertyValues(const style::SymbolLayoutProperties::PossiblyEvaluated&) const;
+
+ std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override;
+ std::unique_ptr<SymbolLayout> createLayout(const BucketParameters&,
+ const std::vector<const RenderLayer*>&,
+ std::unique_ptr<GeometryTileLayer>,
+ GlyphDependencies&,
+ ImageDependencies&) const;
+
+ // Paint properties
+ style::SymbolPaintProperties::Unevaluated unevaluated;
+ style::SymbolPaintProperties::PossiblyEvaluated evaluated;
+
+ float iconSize = 1.0f;
+ float textSize = 16.0f;
+
+ const style::SymbolLayer::Impl& impl() const;
+};
+
+template <>
+inline bool RenderLayer::is<RenderSymbolLayer>() const {
+ return type == style::LayerType::Symbol;
+}
+
+} // namespace mbgl