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