1 #include <mbgl/renderer/layers/render_heatmap_layer.hpp>
2 #include <mbgl/renderer/buckets/heatmap_bucket.hpp>
3 #include <mbgl/renderer/render_tile.hpp>
4 #include <mbgl/renderer/paint_parameters.hpp>
5 #include <mbgl/renderer/render_static_data.hpp>
6 #include <mbgl/programs/programs.hpp>
7 #include <mbgl/programs/heatmap_program.hpp>
8 #include <mbgl/tile/tile.hpp>
9 #include <mbgl/style/layers/heatmap_layer.hpp>
10 #include <mbgl/style/layers/heatmap_layer_impl.hpp>
11 #include <mbgl/geometry/feature_index.hpp>
12 #include <mbgl/util/math.hpp>
13 #include <mbgl/util/intersection_tests.hpp>
14
15 namespace mbgl {
16
17 using namespace style;
18
RenderHeatmapLayer(Immutable<style::HeatmapLayer::Impl> _impl)19 RenderHeatmapLayer::RenderHeatmapLayer(Immutable<style::HeatmapLayer::Impl> _impl)
20 : RenderLayer(style::LayerType::Heatmap, _impl),
21 unevaluated(impl().paint.untransitioned()), colorRamp({256, 1}) {
22 }
23
impl() const24 const style::HeatmapLayer::Impl& RenderHeatmapLayer::impl() const {
25 return static_cast<const style::HeatmapLayer::Impl&>(*baseImpl);
26 }
27
createBucket(const BucketParameters & parameters,const std::vector<const RenderLayer * > & layers) const28 std::unique_ptr<Bucket> RenderHeatmapLayer::createBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) const {
29 return std::make_unique<HeatmapBucket>(parameters, layers);
30 }
31
transition(const TransitionParameters & parameters)32 void RenderHeatmapLayer::transition(const TransitionParameters& parameters) {
33 unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated));
34 }
35
evaluate(const PropertyEvaluationParameters & parameters)36 void RenderHeatmapLayer::evaluate(const PropertyEvaluationParameters& parameters) {
37 evaluated = unevaluated.evaluate(parameters);
38
39 passes = (evaluated.get<style::HeatmapOpacity>() > 0)
40 ? (RenderPass::Translucent | RenderPass::Pass3D)
41 : RenderPass::None;
42 }
43
hasTransition() const44 bool RenderHeatmapLayer::hasTransition() const {
45 return unevaluated.hasTransition();
46 }
47
render(PaintParameters & parameters,RenderSource *)48 void RenderHeatmapLayer::render(PaintParameters& parameters, RenderSource*) {
49 if (parameters.pass == RenderPass::Opaque) {
50 return;
51 }
52
53 if (parameters.pass == RenderPass::Pass3D) {
54 const auto& viewportSize = parameters.staticData.backendSize;
55 const auto size = Size{viewportSize.width / 4, viewportSize.height / 4};
56
57 if (!renderTexture || renderTexture->getSize() != size) {
58 if (parameters.context.supportsHalfFloatTextures) {
59 renderTexture = OffscreenTexture(parameters.context, size, gl::TextureType::HalfFloat);
60
61 try {
62 renderTexture->bind();
63 } catch (const std::runtime_error& ex) {
64 // can't render to a half-float texture; falling back to unsigned byte one
65 renderTexture = nullopt;
66 parameters.context.supportsHalfFloatTextures = false;
67 }
68 }
69
70 if (!parameters.context.supportsHalfFloatTextures || !renderTexture) {
71 renderTexture = OffscreenTexture(parameters.context, size, gl::TextureType::UnsignedByte);
72 renderTexture->bind();
73 }
74
75 } else {
76 renderTexture->bind();
77 }
78
79 if (!colorRampTexture) {
80 colorRampTexture = parameters.context.createTexture(colorRamp, 1, gl::TextureType::UnsignedByte);
81 }
82
83 parameters.context.clear(Color{ 0.0f, 0.0f, 0.0f, 1.0f }, {}, {});
84
85 for (const RenderTile& tile : renderTiles) {
86 auto bucket_ = tile.tile.getBucket<HeatmapBucket>(*baseImpl);
87 if (!bucket_) {
88 continue;
89 }
90 HeatmapBucket& bucket = *bucket_;
91
92 const auto extrudeScale = tile.id.pixelsToTileUnits(1, parameters.state.getZoom());
93
94 const auto stencilMode = parameters.mapMode != MapMode::Continuous
95 ? parameters.stencilModeForClipping(tile.clip)
96 : gl::StencilMode::disabled();
97
98 const auto& paintPropertyBinders = bucket.paintPropertyBinders.at(getID());
99
100 auto& programInstance = parameters.programs.heatmap.get(evaluated);
101
102 const auto allUniformValues = programInstance.computeAllUniformValues(
103 HeatmapProgram::UniformValues {
104 uniforms::u_intensity::Value{ evaluated.get<style::HeatmapIntensity>() },
105 uniforms::u_matrix::Value{ tile.matrix },
106 uniforms::heatmap::u_extrude_scale::Value{ extrudeScale }
107 },
108 paintPropertyBinders,
109 evaluated,
110 parameters.state.getZoom()
111 );
112 const auto allAttributeBindings = programInstance.computeAllAttributeBindings(
113 *bucket.vertexBuffer,
114 paintPropertyBinders,
115 evaluated
116 );
117
118 checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings));
119
120 programInstance.draw(
121 parameters.context,
122 gl::Triangles(),
123 parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly),
124 stencilMode,
125 gl::ColorMode::additive(),
126 *bucket.indexBuffer,
127 bucket.segments,
128 allUniformValues,
129 allAttributeBindings,
130 getID()
131 );
132 }
133
134 } else if (parameters.pass == RenderPass::Translucent) {
135 parameters.context.bindTexture(renderTexture->getTexture(), 0, gl::TextureFilter::Linear);
136 parameters.context.bindTexture(*colorRampTexture, 1, gl::TextureFilter::Linear);
137
138 const auto& size = parameters.staticData.backendSize;
139
140 mat4 viewportMat;
141 matrix::ortho(viewportMat, 0, size.width, size.height, 0, 0, 1);
142
143 const Properties<>::PossiblyEvaluated properties;
144 const HeatmapTextureProgram::PaintPropertyBinders paintAttributeData{ properties, 0 };
145
146 auto& programInstance = parameters.programs.heatmapTexture;
147
148 const auto allUniformValues = programInstance.computeAllUniformValues(
149 HeatmapTextureProgram::UniformValues{
150 uniforms::u_matrix::Value{ viewportMat }, uniforms::u_world::Value{ size },
151 uniforms::u_image::Value{ 0 },
152 uniforms::u_color_ramp::Value{ 1 },
153 uniforms::u_opacity::Value{ evaluated.get<HeatmapOpacity>() }
154 },
155 paintAttributeData,
156 properties,
157 parameters.state.getZoom()
158 );
159 const auto allAttributeBindings = programInstance.computeAllAttributeBindings(
160 parameters.staticData.extrusionTextureVertexBuffer,
161 paintAttributeData,
162 properties
163 );
164
165 checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings));
166
167 programInstance.draw(
168 parameters.context,
169 gl::Triangles(),
170 gl::DepthMode::disabled(),
171 gl::StencilMode::disabled(),
172 parameters.colorModeForRenderPass(),
173 parameters.staticData.quadTriangleIndexBuffer,
174 parameters.staticData.extrusionTextureSegments,
175 allUniformValues,
176 allAttributeBindings,
177 getID()
178 );
179 }
180 }
181
updateColorRamp()182 void RenderHeatmapLayer::updateColorRamp() {
183 auto colorValue = unevaluated.get<HeatmapColor>().getValue();
184 if (colorValue.isUndefined()) {
185 colorValue = HeatmapLayer::getDefaultHeatmapColor();
186 }
187
188 const auto length = colorRamp.bytes();
189
190 for (uint32_t i = 0; i < length; i += 4) {
191 const auto color = colorValue.evaluate(static_cast<double>(i) / length);
192 colorRamp.data[i + 0] = std::floor(color.r * 255);
193 colorRamp.data[i + 1] = std::floor(color.g * 255);
194 colorRamp.data[i + 2] = std::floor(color.b * 255);
195 colorRamp.data[i + 3] = std::floor(color.a * 255);
196 }
197
198 if (colorRampTexture) {
199 colorRampTexture = nullopt;
200 }
201 }
202
queryIntersectsFeature(const GeometryCoordinates & queryGeometry,const GeometryTileFeature & feature,const float zoom,const TransformState &,const float pixelsToTileUnits,const mat4 &) const203 bool RenderHeatmapLayer::queryIntersectsFeature(
204 const GeometryCoordinates& queryGeometry,
205 const GeometryTileFeature& feature,
206 const float zoom,
207 const TransformState&,
208 const float pixelsToTileUnits,
209 const mat4&) const {
210 (void) queryGeometry;
211 (void) feature;
212 (void) zoom;
213 (void) pixelsToTileUnits;
214 return false;
215 }
216
217 } // namespace mbgl
218