diff --git a/CMakeLists.txt b/CMakeLists.txt index 9d01401f7..63bea2250 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -174,6 +174,7 @@ if(MLN_DRAWABLE_RENDERER) ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/drawable_builder_impl.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/drawable_atlases_tweaker.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/drawable_custom_layer_host_tweaker.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/hillshade_prepare_drawable_data.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/image_drawable_data.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/line_drawable_data.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/symbol_drawable_data.hpp @@ -194,16 +195,20 @@ if(MLN_DRAWABLE_RENDERER) ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/fill_layer_tweaker.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/fill_extrusion_layer_tweaker.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/fill_extrusion_layer_tweaker.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/heatmap_layer_tweaker.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/heatmap_layer_tweaker.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/heatmap_texture_layer_tweaker.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/heatmap_texture_layer_tweaker.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/hillshade_layer_tweaker.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/hillshade_layer_tweaker.hpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/hillshade_prepare_layer_tweaker.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/hillshade_prepare_layer_tweaker.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/line_layer_tweaker.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/line_layer_tweaker.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/raster_layer_tweaker.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/raster_layer_tweaker.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/symbol_layer_tweaker.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/symbol_layer_tweaker.hpp - ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/heatmap_layer_tweaker.cpp - ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/heatmap_layer_tweaker.hpp - ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/heatmap_texture_layer_tweaker.cpp - ${PROJECT_SOURCE_DIR}/src/mbgl/renderer/layers/heatmap_texture_layer_tweaker.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/shaders/shader_program_base.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/shaders/mtl/shader_program_mtl.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/util/identity.cpp @@ -266,7 +271,6 @@ list(APPEND INCLUDE_FILES ${PROJECT_SOURCE_DIR}/include/mbgl/platform/thread.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/renderer/query.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/renderer/renderer.hpp - ${PROJECT_SOURCE_DIR}/include/mbgl/renderer/layer_tweaker.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/renderer/renderer_frontend.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/renderer/renderer_observer.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/renderer/renderer_state.hpp @@ -1011,6 +1015,8 @@ if(MLN_WITH_OPENGL) ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/drawable_fill_extrusion_pattern.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/drawable_heatmap.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/drawable_heatmap_texture.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/drawable_hillshade_prepare.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/drawable_hillshade.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/drawable_raster.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/drawable_symbol_icon.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/drawable_symbol_sdf_text.hpp diff --git a/bazel/core.bzl b/bazel/core.bzl index acee701ac..9d61ef5ad 100644 --- a/bazel/core.bzl +++ b/bazel/core.bzl @@ -68,12 +68,14 @@ MLN_GENERATED_SHADER_HEADERS = [ "include/mbgl/shaders/gl/drawable_fill_extrusion.hpp", "include/mbgl/shaders/gl/drawable_fill_extrusion_pattern.hpp", "include/mbgl/shaders/gl/drawable_fill_pattern.hpp", + "include/mbgl/shaders/gl/drawable_heatmap.hpp", + "include/mbgl/shaders/gl/drawable_heatmap_texture.hpp", + "include/mbgl/shaders/gl/drawable_hillshade_prepare.hpp", + "include/mbgl/shaders/gl/drawable_hillshade.hpp", "include/mbgl/shaders/gl/drawable_line_gradient.hpp", "include/mbgl/shaders/gl/drawable_line_pattern.hpp", "include/mbgl/shaders/gl/drawable_line_sdf.hpp", "include/mbgl/shaders/gl/drawable_line.hpp", - "include/mbgl/shaders/gl/drawable_heatmap.hpp", - "include/mbgl/shaders/gl/drawable_heatmap_texture.hpp", "include/mbgl/shaders/gl/drawable_raster.hpp", "include/mbgl/shaders/gl/drawable_symbol_icon.hpp", "include/mbgl/shaders/gl/drawable_symbol_sdf_text.hpp", @@ -881,7 +883,7 @@ MLN_OPENGL_HEADERS = [ "include/mbgl/gl/renderable_resource.hpp", "include/mbgl/gl/renderer_backend.hpp", "include/mbgl/layermanager/location_indicator_layer_factory.hpp", - "include/mbgl/platform/gl_functions.hpp", + "include/mbgl/platform/gl_functions.hpp" ] MLN_DRAWABLES_SOURCE = [ @@ -890,6 +892,7 @@ MLN_DRAWABLES_SOURCE = [ "src/mbgl/gfx/drawable_builder_impl.hpp", "src/mbgl/gfx/drawable_atlases_tweaker.cpp", "src/mbgl/gfx/drawable_custom_layer_host_tweaker.cpp", + "src/mbgl/gfx/hillshade_prepare_drawable_data.hpp", "src/mbgl/gfx/image_drawable_data.hpp", "src/mbgl/gfx/line_drawable_data.hpp", "src/mbgl/gfx/symbol_drawable_data.hpp", @@ -913,6 +916,10 @@ MLN_DRAWABLES_SOURCE = [ "src/mbgl/renderer/layers/heatmap_layer_tweaker.hpp", "src/mbgl/renderer/layers/heatmap_texture_layer_tweaker.cpp", "src/mbgl/renderer/layers/heatmap_texture_layer_tweaker.hpp", + "src/mbgl/renderer/layers/hillshade_layer_tweaker.cpp", + "src/mbgl/renderer/layers/hillshade_layer_tweaker.hpp", + "src/mbgl/renderer/layers/hillshade_prepare_layer_tweaker.cpp", + "src/mbgl/renderer/layers/hillshade_prepare_layer_tweaker.hpp", "src/mbgl/renderer/layers/line_layer_tweaker.cpp", "src/mbgl/renderer/layers/line_layer_tweaker.hpp", "src/mbgl/renderer/layers/raster_layer_tweaker.cpp", diff --git a/include/mbgl/gfx/drawable.hpp b/include/mbgl/gfx/drawable.hpp index a5dc77842..97a064da6 100644 --- a/include/mbgl/gfx/drawable.hpp +++ b/include/mbgl/gfx/drawable.hpp @@ -151,6 +151,13 @@ public: virtual void setVertexAttributes(const gfx::VertexAttributeArray&) = 0; virtual void setVertexAttributes(gfx::VertexAttributeArray&&) = 0; + /// Provide raw data for vertices + /// Incompatible with adding primitives + virtual void setVertices(std::vector&&, std::size_t, AttributeDataType) = 0; + + /// Create a segment wrapper + virtual void setIndexData(std::vector indexes, std::vector segments) = 0; + /// Get the tweakers attached to this drawable const std::vector& getTweakers() const { return tweakers; } void addTweaker(DrawableTweakerPtr value) { tweakers.emplace_back(std::move(value)); } diff --git a/include/mbgl/gfx/drawable_builder.hpp b/include/mbgl/gfx/drawable_builder.hpp index 1fc575e29..e260cf735 100644 --- a/include/mbgl/gfx/drawable_builder.hpp +++ b/include/mbgl/gfx/drawable_builder.hpp @@ -152,15 +152,15 @@ public: std::size_t indexLength, std::size_t baseIndex = 0); + /// Create a segment wrapper + virtual std::unique_ptr createSegment(gfx::DrawMode, SegmentBase&&) = 0; + protected: std::size_t curVertexCount() const; /// Create an instance of the appropriate drawable type virtual UniqueDrawable createDrawable() const = 0; - /// Create a segment wrapper - virtual std::unique_ptr createSegment(gfx::DrawMode, SegmentBase&&) = 0; - /// Setup the SDK-specific aspects after all the values are present virtual void init() = 0; diff --git a/include/mbgl/gl/drawable_gl.hpp b/include/mbgl/gl/drawable_gl.hpp index ab2142fd2..34ca129b7 100644 --- a/include/mbgl/gl/drawable_gl.hpp +++ b/include/mbgl/gl/drawable_gl.hpp @@ -36,9 +36,9 @@ public: void draw(PaintParameters&) const override; struct DrawSegmentGL; - void setIndexData(std::vector indexes, std::vector segments); + void setIndexData(std::vector indexes, std::vector segments) override; - void setVertices(std::vector&&, std::size_t, gfx::AttributeDataType); + void setVertices(std::vector&&, std::size_t, gfx::AttributeDataType) override; const gfx::VertexAttributeArray& getVertexAttributes() const override; void setVertexAttributes(const gfx::VertexAttributeArray& value) override; diff --git a/include/mbgl/gl/drawable_gl_builder.hpp b/include/mbgl/gl/drawable_gl_builder.hpp index 089b67a22..4b7e02e2b 100644 --- a/include/mbgl/gl/drawable_gl_builder.hpp +++ b/include/mbgl/gl/drawable_gl_builder.hpp @@ -15,12 +15,12 @@ public: : gfx::DrawableBuilder(std::move(name_)) {} ~DrawableGLBuilder() override = default; -protected: - gfx::UniqueDrawable createDrawable() const override; - using DrawSegment = gfx::Drawable::DrawSegment; std::unique_ptr createSegment(gfx::DrawMode, SegmentBase&&) override; +protected: + gfx::UniqueDrawable createDrawable() const override; + /// Setup the SDK-specific aspects after all the values are present void init() override; }; diff --git a/include/mbgl/renderer/change_request.hpp b/include/mbgl/renderer/change_request.hpp index ff36bf999..79e3475ca 100644 --- a/include/mbgl/renderer/change_request.hpp +++ b/include/mbgl/renderer/change_request.hpp @@ -109,12 +109,12 @@ protected: */ class RemoveRenderTargetRequest : public ChangeRequest { public: - RemoveRenderTargetRequest(const RenderTargetPtr &renderTarget_); + RemoveRenderTargetRequest(RenderTargetPtr renderTarget_); void execute(RenderOrchestrator &) override; protected: - const RenderTargetPtr &renderTarget; + RenderTargetPtr renderTarget; }; } // namespace mbgl diff --git a/include/mbgl/renderer/layer_tweaker.hpp b/include/mbgl/renderer/layer_tweaker.hpp index b9650e42e..f01e201bf 100644 --- a/include/mbgl/renderer/layer_tweaker.hpp +++ b/include/mbgl/renderer/layer_tweaker.hpp @@ -45,7 +45,8 @@ protected: const std::array& translation, style::TranslateAnchorType, bool nearClipped, - bool inViewportPixelUnits); + bool inViewportPixelUnits, + bool aligned = false); protected: Immutable evaluatedProperties; diff --git a/include/mbgl/shaders/gl/drawable_hillshade.hpp b/include/mbgl/shaders/gl/drawable_hillshade.hpp new file mode 100644 index 000000000..3d706de1a --- /dev/null +++ b/include/mbgl/shaders/gl/drawable_hillshade.hpp @@ -0,0 +1,89 @@ +// Generated code, do not modify this file! +#pragma once +#include + +namespace mbgl { +namespace shaders { + +template <> +struct ShaderSource { + static constexpr const char* name = "HillshadeShader"; + static constexpr const char* vertex = R"(layout (location = 0) in vec2 a_pos; +layout (location = 1) in vec2 a_texture_pos; + +layout (std140) uniform HillshadeDrawableUBO { + highp mat4 u_matrix; + highp vec2 u_latrange; + highp vec2 u_light; +}; + +out vec2 v_pos; + +void main() { + gl_Position = u_matrix * vec4(a_pos, 0, 1); + v_pos = a_texture_pos / 8192.0; +} +)"; + static constexpr const char* fragment = R"(in vec2 v_pos; +uniform sampler2D u_image; + +layout (std140) uniform HillshadeDrawableUBO { + highp mat4 u_matrix; + highp vec2 u_latrange; + highp vec2 u_light; +}; + +layout (std140) uniform HillshadeEvaluatedPropsUBO { + highp vec4 u_highlight; + highp vec4 u_shadow; + highp vec4 u_accent; +}; + +#define PI 3.141592653589793 + +void main() { + vec4 pixel = texture(u_image, v_pos); + + vec2 deriv = ((pixel.rg * 2.0) - 1.0); + + // We divide the slope by a scale factor based on the cosin of the pixel's approximate latitude + // to account for mercator projection distortion. see #4807 for details + float scaleFactor = cos(radians((u_latrange[0] - u_latrange[1]) * (1.0 - v_pos.y) + u_latrange[1])); + // We also multiply the slope by an arbitrary z-factor of 1.25 + float slope = atan(1.25 * length(deriv) / scaleFactor); + float aspect = deriv.x != 0.0 ? atan(deriv.y, -deriv.x) : PI / 2.0 * (deriv.y > 0.0 ? 1.0 : -1.0); + + float intensity = u_light.x; + // We add PI to make this property match the global light object, which adds PI/2 to the light's azimuthal + // position property to account for 0deg corresponding to north/the top of the viewport in the style spec + // and the original shader was written to accept (-illuminationDirection - 90) as the azimuthal. + float azimuth = u_light.y + PI; + + // We scale the slope exponentially based on intensity, using a calculation similar to + // the exponential interpolation function in the style spec: + // https://github.com/mapbox/mapbox-gl-js/blob/master/src/style-spec/expression/definitions/interpolate.js#L217-L228 + // so that higher intensity values create more opaque hillshading. + float base = 1.875 - intensity * 1.75; + float maxValue = 0.5 * PI; + float scaledSlope = intensity != 0.5 ? ((pow(base, slope) - 1.0) / (pow(base, maxValue) - 1.0)) * maxValue : slope; + + // The accent color is calculated with the cosine of the slope while the shade color is calculated with the sine + // so that the accent color's rate of change eases in while the shade color's eases out. + float accent = cos(scaledSlope); + // We multiply both the accent and shade color by a clamped intensity value + // so that intensities >= 0.5 do not additionally affect the color values + // while intensity values < 0.5 make the overall color more transparent. + vec4 accent_color = (1.0 - accent) * u_accent * clamp(intensity * 2.0, 0.0, 1.0); + float shade = abs(mod((aspect + azimuth) / PI + 0.5, 2.0) - 1.0); + vec4 shade_color = mix(u_shadow, u_highlight, shade) * sin(scaledSlope) * clamp(intensity * 2.0, 0.0, 1.0); + fragColor = accent_color * (1.0 - shade_color.a) + shade_color; + +#ifdef OVERDRAW_INSPECTOR + fragColor = vec4(1.0); +#endif +} +)"; +}; + +} // namespace shaders +} // namespace mbgl diff --git a/include/mbgl/shaders/gl/drawable_hillshade_prepare.hpp b/include/mbgl/shaders/gl/drawable_hillshade_prepare.hpp new file mode 100644 index 000000000..8cfbb8677 --- /dev/null +++ b/include/mbgl/shaders/gl/drawable_hillshade_prepare.hpp @@ -0,0 +1,114 @@ +// Generated code, do not modify this file! +#pragma once +#include + +namespace mbgl { +namespace shaders { + +template <> +struct ShaderSource { + static constexpr const char* name = "HillshadePrepareShader"; + static constexpr const char* vertex = R"(layout (location = 0) in vec2 a_pos; +layout (location = 1) in vec2 a_texture_pos; + +layout (std140) uniform HillshadePrepareDrawableUBO { + highp mat4 u_matrix; + highp vec4 u_unpack; + highp vec2 u_dimension; + highp float u_zoom; + highp float u_maxzoom; +}; + +out vec2 v_pos; + +void main() { + gl_Position = u_matrix * vec4(a_pos, 0, 1); + + highp vec2 epsilon = 1.0 / u_dimension; + float scale = (u_dimension.x - 2.0) / u_dimension.x; + v_pos = (a_texture_pos / 8192.0) * scale + epsilon; +} +)"; + static constexpr const char* fragment = R"(#ifdef GL_ES +precision highp float; +#endif + +in vec2 v_pos; +uniform sampler2D u_image; + +layout (std140) uniform HillshadePrepareDrawableUBO { + highp mat4 u_matrix; + highp vec4 u_unpack; + highp vec2 u_dimension; + highp float u_zoom; + highp float u_maxzoom; +}; + +float getElevation(vec2 coord, float bias) { + // Convert encoded elevation value to meters + vec4 data = texture(u_image, coord) * 255.0; + data.a = -1.0; + return dot(data, u_unpack) / 4.0; +} + +void main() { + vec2 epsilon = 1.0 / u_dimension; + + // queried pixels: + // +-----------+ + // | | | | + // | a | b | c | + // | | | | + // +-----------+ + // | | | | + // | d | e | f | + // | | | | + // +-----------+ + // | | | | + // | g | h | i | + // | | | | + // +-----------+ + + float a = getElevation(v_pos + vec2(-epsilon.x, -epsilon.y), 0.0); + float b = getElevation(v_pos + vec2(0, -epsilon.y), 0.0); + float c = getElevation(v_pos + vec2(epsilon.x, -epsilon.y), 0.0); + float d = getElevation(v_pos + vec2(-epsilon.x, 0), 0.0); + float e = getElevation(v_pos, 0.0); + float f = getElevation(v_pos + vec2(epsilon.x, 0), 0.0); + float g = getElevation(v_pos + vec2(-epsilon.x, epsilon.y), 0.0); + float h = getElevation(v_pos + vec2(0, epsilon.y), 0.0); + float i = getElevation(v_pos + vec2(epsilon.x, epsilon.y), 0.0); + + // here we divide the x and y slopes by 8 * pixel size + // where pixel size (aka meters/pixel) is: + // circumference of the world / (pixels per tile * number of tiles) + // which is equivalent to: 8 * 40075016.6855785 / (512 * pow(2, u_zoom)) + // which can be reduced to: pow(2, 19.25619978527 - u_zoom) + // we want to vertically exaggerate the hillshading though, because otherwise + // it is barely noticeable at low zooms. to do this, we multiply this by some + // scale factor pow(2, (u_zoom - u_maxzoom) * a) where a is an arbitrary value + // Here we use a=0.3 which works out to the expression below. see + // nickidlugash's awesome breakdown for more info + // https://github.com/mapbox/mapbox-gl-js/pull/5286#discussion_r148419556 + float exaggeration = u_zoom < 2.0 ? 0.4 : u_zoom < 4.5 ? 0.35 : 0.3; + + vec2 deriv = vec2( + (c + f + f + i) - (a + d + d + g), + (g + h + h + i) - (a + b + b + c) + ) / pow(2.0, (u_zoom - u_maxzoom) * exaggeration + 19.2562 - u_zoom); + + fragColor = clamp(vec4( + deriv.x / 2.0 + 0.5, + deriv.y / 2.0 + 0.5, + 1.0, + 1.0), 0.0, 1.0); + +#ifdef OVERDRAW_INSPECTOR + fragColor = vec4(1.0); +#endif +} +)"; +}; + +} // namespace shaders +} // namespace mbgl diff --git a/include/mbgl/shaders/shader_manifest.hpp b/include/mbgl/shaders/shader_manifest.hpp index 3bda7348d..fccdb72c2 100644 --- a/include/mbgl/shaders/shader_manifest.hpp +++ b/include/mbgl/shaders/shader_manifest.hpp @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/include/mbgl/shaders/shader_source.hpp b/include/mbgl/shaders/shader_source.hpp index be0f60e8a..d09c188ae 100644 --- a/include/mbgl/shaders/shader_source.hpp +++ b/include/mbgl/shaders/shader_source.hpp @@ -24,6 +24,8 @@ enum class BuiltIn { FillExtrusionPatternShader, HeatmapShader, HeatmapTextureShader, + HillshadePrepareShader, + HillshadeShader, RasterShader, SymbolIconShader, SymbolSDFTextShader, diff --git a/shaders/drawable.hillshade.fragment.glsl b/shaders/drawable.hillshade.fragment.glsl new file mode 100644 index 000000000..ca3df9255 --- /dev/null +++ b/shaders/drawable.hillshade.fragment.glsl @@ -0,0 +1,58 @@ +in vec2 v_pos; +uniform sampler2D u_image; + +layout (std140) uniform HillshadeDrawableUBO { + highp mat4 u_matrix; + highp vec2 u_latrange; + highp vec2 u_light; +}; + +layout (std140) uniform HillshadeEvaluatedPropsUBO { + highp vec4 u_highlight; + highp vec4 u_shadow; + highp vec4 u_accent; +}; + +#define PI 3.141592653589793 + +void main() { + vec4 pixel = texture(u_image, v_pos); + + vec2 deriv = ((pixel.rg * 2.0) - 1.0); + + // We divide the slope by a scale factor based on the cosin of the pixel's approximate latitude + // to account for mercator projection distortion. see #4807 for details + float scaleFactor = cos(radians((u_latrange[0] - u_latrange[1]) * (1.0 - v_pos.y) + u_latrange[1])); + // We also multiply the slope by an arbitrary z-factor of 1.25 + float slope = atan(1.25 * length(deriv) / scaleFactor); + float aspect = deriv.x != 0.0 ? atan(deriv.y, -deriv.x) : PI / 2.0 * (deriv.y > 0.0 ? 1.0 : -1.0); + + float intensity = u_light.x; + // We add PI to make this property match the global light object, which adds PI/2 to the light's azimuthal + // position property to account for 0deg corresponding to north/the top of the viewport in the style spec + // and the original shader was written to accept (-illuminationDirection - 90) as the azimuthal. + float azimuth = u_light.y + PI; + + // We scale the slope exponentially based on intensity, using a calculation similar to + // the exponential interpolation function in the style spec: + // https://github.com/mapbox/mapbox-gl-js/blob/master/src/style-spec/expression/definitions/interpolate.js#L217-L228 + // so that higher intensity values create more opaque hillshading. + float base = 1.875 - intensity * 1.75; + float maxValue = 0.5 * PI; + float scaledSlope = intensity != 0.5 ? ((pow(base, slope) - 1.0) / (pow(base, maxValue) - 1.0)) * maxValue : slope; + + // The accent color is calculated with the cosine of the slope while the shade color is calculated with the sine + // so that the accent color's rate of change eases in while the shade color's eases out. + float accent = cos(scaledSlope); + // We multiply both the accent and shade color by a clamped intensity value + // so that intensities >= 0.5 do not additionally affect the color values + // while intensity values < 0.5 make the overall color more transparent. + vec4 accent_color = (1.0 - accent) * u_accent * clamp(intensity * 2.0, 0.0, 1.0); + float shade = abs(mod((aspect + azimuth) / PI + 0.5, 2.0) - 1.0); + vec4 shade_color = mix(u_shadow, u_highlight, shade) * sin(scaledSlope) * clamp(intensity * 2.0, 0.0, 1.0); + fragColor = accent_color * (1.0 - shade_color.a) + shade_color; + +#ifdef OVERDRAW_INSPECTOR + fragColor = vec4(1.0); +#endif +} diff --git a/shaders/drawable.hillshade.vertex.glsl b/shaders/drawable.hillshade.vertex.glsl new file mode 100644 index 000000000..20fadb2f0 --- /dev/null +++ b/shaders/drawable.hillshade.vertex.glsl @@ -0,0 +1,15 @@ +layout (location = 0) in vec2 a_pos; +layout (location = 1) in vec2 a_texture_pos; + +layout (std140) uniform HillshadeDrawableUBO { + highp mat4 u_matrix; + highp vec2 u_latrange; + highp vec2 u_light; +}; + +out vec2 v_pos; + +void main() { + gl_Position = u_matrix * vec4(a_pos, 0, 1); + v_pos = a_texture_pos / 8192.0; +} diff --git a/shaders/drawable.hillshade_prepare.fragment.glsl b/shaders/drawable.hillshade_prepare.fragment.glsl new file mode 100644 index 000000000..49502d917 --- /dev/null +++ b/shaders/drawable.hillshade_prepare.fragment.glsl @@ -0,0 +1,78 @@ +#ifdef GL_ES +precision highp float; +#endif + +in vec2 v_pos; +uniform sampler2D u_image; + +layout (std140) uniform HillshadePrepareDrawableUBO { + highp mat4 u_matrix; + highp vec4 u_unpack; + highp vec2 u_dimension; + highp float u_zoom; + highp float u_maxzoom; +}; + +float getElevation(vec2 coord, float bias) { + // Convert encoded elevation value to meters + vec4 data = texture(u_image, coord) * 255.0; + data.a = -1.0; + return dot(data, u_unpack) / 4.0; +} + +void main() { + vec2 epsilon = 1.0 / u_dimension; + + // queried pixels: + // +-----------+ + // | | | | + // | a | b | c | + // | | | | + // +-----------+ + // | | | | + // | d | e | f | + // | | | | + // +-----------+ + // | | | | + // | g | h | i | + // | | | | + // +-----------+ + + float a = getElevation(v_pos + vec2(-epsilon.x, -epsilon.y), 0.0); + float b = getElevation(v_pos + vec2(0, -epsilon.y), 0.0); + float c = getElevation(v_pos + vec2(epsilon.x, -epsilon.y), 0.0); + float d = getElevation(v_pos + vec2(-epsilon.x, 0), 0.0); + float e = getElevation(v_pos, 0.0); + float f = getElevation(v_pos + vec2(epsilon.x, 0), 0.0); + float g = getElevation(v_pos + vec2(-epsilon.x, epsilon.y), 0.0); + float h = getElevation(v_pos + vec2(0, epsilon.y), 0.0); + float i = getElevation(v_pos + vec2(epsilon.x, epsilon.y), 0.0); + + // here we divide the x and y slopes by 8 * pixel size + // where pixel size (aka meters/pixel) is: + // circumference of the world / (pixels per tile * number of tiles) + // which is equivalent to: 8 * 40075016.6855785 / (512 * pow(2, u_zoom)) + // which can be reduced to: pow(2, 19.25619978527 - u_zoom) + // we want to vertically exaggerate the hillshading though, because otherwise + // it is barely noticeable at low zooms. to do this, we multiply this by some + // scale factor pow(2, (u_zoom - u_maxzoom) * a) where a is an arbitrary value + // Here we use a=0.3 which works out to the expression below. see + // nickidlugash's awesome breakdown for more info + // https://github.com/mapbox/mapbox-gl-js/pull/5286#discussion_r148419556 + float exaggeration = u_zoom < 2.0 ? 0.4 : u_zoom < 4.5 ? 0.35 : 0.3; + + vec2 deriv = vec2( + (c + f + f + i) - (a + d + d + g), + (g + h + h + i) - (a + b + b + c) + ) / pow(2.0, (u_zoom - u_maxzoom) * exaggeration + 19.2562 - u_zoom); + + fragColor = clamp(vec4( + deriv.x / 2.0 + 0.5, + deriv.y / 2.0 + 0.5, + 1.0, + 1.0), 0.0, 1.0); + +#ifdef OVERDRAW_INSPECTOR + fragColor = vec4(1.0); +#endif +} diff --git a/shaders/drawable.hillshade_prepare.vertex.glsl b/shaders/drawable.hillshade_prepare.vertex.glsl new file mode 100644 index 000000000..a3aaad8d0 --- /dev/null +++ b/shaders/drawable.hillshade_prepare.vertex.glsl @@ -0,0 +1,20 @@ +layout (location = 0) in vec2 a_pos; +layout (location = 1) in vec2 a_texture_pos; + +layout (std140) uniform HillshadePrepareDrawableUBO { + highp mat4 u_matrix; + highp vec4 u_unpack; + highp vec2 u_dimension; + highp float u_zoom; + highp float u_maxzoom; +}; + +out vec2 v_pos; + +void main() { + gl_Position = u_matrix * vec4(a_pos, 0, 1); + + highp vec2 epsilon = 1.0 / u_dimension; + float scale = (u_dimension.x - 2.0) / u_dimension.x; + v_pos = (a_texture_pos / 8192.0) * scale + epsilon; +} diff --git a/shaders/manifest.json b/shaders/manifest.json index 4043772e8..8e27a9b7e 100644 --- a/shaders/manifest.json +++ b/shaders/manifest.json @@ -104,6 +104,20 @@ "glsl_frag": "drawable.heatmap_texture.fragment.glsl", "uses_ubos": true }, + { + "name": "HillshadePrepareShader", + "header": "drawable_hillshade_prepare", + "glsl_vert": "drawable.hillshade_prepare.vertex.glsl", + "glsl_frag": "drawable.hillshade_prepare.fragment.glsl", + "uses_ubos": true + }, + { + "name": "HillshadeShader", + "header": "drawable_hillshade", + "glsl_vert": "drawable.hillshade.vertex.glsl", + "glsl_frag": "drawable.hillshade.fragment.glsl", + "uses_ubos": true + }, { "name": "RasterShader", "header": "drawable_raster", diff --git a/src/mbgl/geometry/dem_data.cpp b/src/mbgl/geometry/dem_data.cpp index 55ea5cbd6..81032006f 100644 --- a/src/mbgl/geometry/dem_data.cpp +++ b/src/mbgl/geometry/dem_data.cpp @@ -7,13 +7,13 @@ DEMData::DEMData(const PremultipliedImage& _image, Tileset::DEMEncoding _encodin : dim(_image.size.height), // extra two pixels per row for border backfilling on either edge stride(dim + 2), - encoding(_encoding), - image({static_cast(stride), static_cast(stride)}) { + encoding(_encoding) { + image = std::make_shared(Size(static_cast(stride), static_cast(stride))); if (_image.size.height != _image.size.width) { throw std::runtime_error("raster-dem tiles must be square."); } - auto* dest = reinterpret_cast(image.data.get()) + stride + 1; + auto* dest = reinterpret_cast(image->data.get()) + stride + 1; auto* source = reinterpret_cast(_image.data.get()); for (int32_t y = 0; y < dim; y++) { memcpy(dest, source, dim * 4); @@ -27,7 +27,7 @@ DEMData::DEMData(const PremultipliedImage& _image, Tileset::DEMEncoding _encodin // tile's neighboring tiles are loaded and the accurate data can be // backfilled using DEMData#backfillBorder - auto* data = reinterpret_cast(image.data.get()); + auto* data = reinterpret_cast(image->data.get()); for (int32_t x = 0; x < dim; x++) { auto rowOffset = stride * (x + 1); // left vertical border @@ -77,8 +77,8 @@ void DEMData::backfillBorder(const DEMData& borderTileData, int8_t dx, int8_t dy int32_t ox = -dx * dim; int32_t oy = -dy * dim; - auto* dest = reinterpret_cast(image.data.get()); - auto* source = reinterpret_cast(o.image.data.get()); + auto* dest = reinterpret_cast(image->data.get()); + auto* source = reinterpret_cast(o.image->data.get()); for (int32_t y = yMin; y < yMax; y++) { for (int32_t x = xMin; x < xMax; x++) { @@ -89,7 +89,7 @@ void DEMData::backfillBorder(const DEMData& borderTileData, int8_t dx, int8_t dy int32_t DEMData::get(const int32_t x, const int32_t y) const { const auto& unpack = getUnpackVector(); - const uint8_t* value = image.data.get() + idx(x, y) * 4; + const uint8_t* value = image->data.get() + idx(x, y) * 4; return static_cast(value[0] * unpack[0] + value[1] * unpack[1] + value[2] * unpack[2] - unpack[3]); } diff --git a/src/mbgl/geometry/dem_data.hpp b/src/mbgl/geometry/dem_data.hpp index b087afb63..81d21e45a 100644 --- a/src/mbgl/geometry/dem_data.hpp +++ b/src/mbgl/geometry/dem_data.hpp @@ -19,14 +19,15 @@ public: int32_t get(int32_t x, int32_t y) const; const std::array& getUnpackVector() const; - const PremultipliedImage* getImage() const { return ℑ } + const PremultipliedImage* getImage() const { return &*image; } + const std::shared_ptr& getImagePtr() const { return image; } const int32_t dim; const int32_t stride; + const Tileset::DEMEncoding encoding; private: - Tileset::DEMEncoding encoding; - PremultipliedImage image; + std::shared_ptr image; size_t idx(const int32_t x, const int32_t y) const { assert(x >= -1); diff --git a/src/mbgl/gfx/hillshade_prepare_drawable_data.hpp b/src/mbgl/gfx/hillshade_prepare_drawable_data.hpp new file mode 100644 index 000000000..26f806c5a --- /dev/null +++ b/src/mbgl/gfx/hillshade_prepare_drawable_data.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +#include + +namespace mbgl { + +namespace gfx { + +class HillshadePrepareDrawableData : public DrawableData { +public: + HillshadePrepareDrawableData(int32_t stride_, Tileset::DEMEncoding encoding_, uint8_t maxzoom_) + : stride(stride_), + encoding(encoding_), + maxzoom(maxzoom_) {} + + int32_t stride; + Tileset::DEMEncoding encoding; + uint8_t maxzoom; +}; + +using UniqueHillshadePrepareDrawableData = std::unique_ptr; + +} // namespace gfx +} // namespace mbgl diff --git a/src/mbgl/gl/renderer_backend.cpp b/src/mbgl/gl/renderer_backend.cpp index 745598bc3..1d1a39c71 100644 --- a/src/mbgl/gl/renderer_backend.cpp +++ b/src/mbgl/gl/renderer_backend.cpp @@ -108,6 +108,8 @@ void RendererBackend::initShaders(gfx::ShaderRegistry& shaders, const ProgramPar shaders::BuiltIn::FillExtrusionPatternShader, shaders::BuiltIn::HeatmapShader, shaders::BuiltIn::HeatmapTextureShader, + shaders::BuiltIn::HillshadePrepareShader, + shaders::BuiltIn::HillshadeShader, shaders::BuiltIn::RasterShader, shaders::BuiltIn::SymbolIconShader, shaders::BuiltIn::SymbolSDFTextShader, diff --git a/src/mbgl/renderer/buckets/hillshade_bucket.cpp b/src/mbgl/renderer/buckets/hillshade_bucket.cpp index dba1831b5..f66cdd873 100644 --- a/src/mbgl/renderer/buckets/hillshade_bucket.cpp +++ b/src/mbgl/renderer/buckets/hillshade_bucket.cpp @@ -14,7 +14,9 @@ HillshadeBucket::HillshadeBucket(PremultipliedImage&& image_, Tileset::DEMEncodi HillshadeBucket::HillshadeBucket(DEMData&& demdata_) : demdata(std::move(demdata_)) {} -HillshadeBucket::~HillshadeBucket() = default; +HillshadeBucket::~HillshadeBucket() { + sharedVertices->release(); +} const DEMData& HillshadeBucket::getDEMData() const { return demdata; diff --git a/src/mbgl/renderer/buckets/hillshade_bucket.hpp b/src/mbgl/renderer/buckets/hillshade_bucket.hpp index caf0632a7..202642228 100644 --- a/src/mbgl/renderer/buckets/hillshade_bucket.hpp +++ b/src/mbgl/renderer/buckets/hillshade_bucket.hpp @@ -30,6 +30,11 @@ public: std::optional dem; std::optional texture; +#if MLN_DRAWABLE_RENDERER + RenderTargetPtr renderTarget; + bool renderTargetPrepared = false; +#endif + TileMask mask{{0, 0, 0}}; const DEMData& getDEMData() const; @@ -40,7 +45,10 @@ public: void setPrepared(bool preparedState) { prepared = preparedState; } // Raster-DEM Tile Sources use the default buffers from Painter - gfx::VertexVector vertices; + using VertexVector = gfx::VertexVector; + std::shared_ptr sharedVertices = std::make_shared(); + VertexVector& vertices = *sharedVertices; + gfx::IndexVector indices; SegmentVector segments; diff --git a/src/mbgl/renderer/change_request.cpp b/src/mbgl/renderer/change_request.cpp index 09e526e07..0d655d3a5 100644 --- a/src/mbgl/renderer/change_request.cpp +++ b/src/mbgl/renderer/change_request.cpp @@ -36,8 +36,8 @@ void AddRenderTargetRequest::execute(RenderOrchestrator &orchestrator) { orchestrator.addRenderTarget(std::move(renderTarget)); } -RemoveRenderTargetRequest::RemoveRenderTargetRequest(const RenderTargetPtr &renderTarget_) - : renderTarget(renderTarget_) {} +RemoveRenderTargetRequest::RemoveRenderTargetRequest(RenderTargetPtr renderTarget_) + : renderTarget(std::move(renderTarget_)) {} void RemoveRenderTargetRequest::execute(RenderOrchestrator &orchestrator) { orchestrator.removeRenderTarget(renderTarget); diff --git a/src/mbgl/renderer/layer_tweaker.cpp b/src/mbgl/renderer/layer_tweaker.cpp index 8cfac84b3..781513c81 100644 --- a/src/mbgl/renderer/layer_tweaker.cpp +++ b/src/mbgl/renderer/layer_tweaker.cpp @@ -17,14 +17,17 @@ mat4 LayerTweaker::getTileMatrix(const UnwrappedTileID& tileID, const std::array& translation, style::TranslateAnchorType anchor, bool nearClipped, - bool inViewportPixelUnits) { + bool inViewportPixelUnits, + bool aligned) { // from RenderTile::prepare mat4 tileMatrix; state.matrixFor(/*out*/ tileMatrix, tileID); + const auto& transformParams = renderTree.getParameters().transformParams; // nearClippedMatrix has near plane moved further, to enhance depth buffer precision - const auto& params = renderTree.getParameters().transformParams; - const auto& projMatrix = nearClipped ? params.nearClippedProjMatrix : params.projMatrix; + const auto& projMatrix = aligned + ? transformParams.alignedProjMatrix + : (nearClipped ? transformParams.nearClippedProjMatrix : transformParams.projMatrix); matrix::multiply(tileMatrix, projMatrix, tileMatrix); return RenderTile::translateVtxMatrix(tileID, tileMatrix, translation, anchor, state, inViewportPixelUnits); diff --git a/src/mbgl/renderer/layers/heatmap_layer_tweaker.hpp b/src/mbgl/renderer/layers/heatmap_layer_tweaker.hpp index d912f98e6..93e1c9084 100644 --- a/src/mbgl/renderer/layers/heatmap_layer_tweaker.hpp +++ b/src/mbgl/renderer/layers/heatmap_layer_tweaker.hpp @@ -18,7 +18,6 @@ public: void execute(LayerGroupBase&, const RenderTree&, const PaintParameters&) override; protected: - gfx::UniformBufferPtr paintParamsUniformBuffer; gfx::UniformBufferPtr evaluatedPropsUniformBuffer; }; diff --git a/src/mbgl/renderer/layers/hillshade_layer_tweaker.cpp b/src/mbgl/renderer/layers/hillshade_layer_tweaker.cpp new file mode 100644 index 000000000..332038c8e --- /dev/null +++ b/src/mbgl/renderer/layers/hillshade_layer_tweaker.cpp @@ -0,0 +1,85 @@ +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace mbgl { + +using namespace style; + +struct alignas(16) HillshadeDrawableUBO { + std::array matrix; + std::array latrange; + std::array light; +}; +static_assert(sizeof(HillshadeDrawableUBO) % 16 == 0); + +struct alignas(16) HillshadeEvaluatedPropsUBO { + Color highlight; + Color shadow; + Color accent; +}; +static_assert(sizeof(HillshadeEvaluatedPropsUBO) % 16 == 0); + +static constexpr std::string_view HillshadeDrawableUBOName = "HillshadeDrawableUBO"; +static constexpr std::string_view HillshadeEvaluatedPropsUBOName = "HillshadeEvaluatedPropsUBO"; + +std::array getLatRange(const UnwrappedTileID& id) { + const LatLng latlng0 = LatLng(id); + const LatLng latlng1 = LatLng(UnwrappedTileID(id.canonical.z, id.canonical.x, id.canonical.y + 1)); + return {{static_cast(latlng0.latitude()), static_cast(latlng1.latitude())}}; +} + +std::array getLight(const PaintParameters& parameters, + const HillshadePaintProperties::PossiblyEvaluated& evaluated) { + float azimuthal = util::deg2radf(evaluated.get()); + if (evaluated.get() == HillshadeIlluminationAnchorType::Viewport) + azimuthal = azimuthal - static_cast(parameters.state.getBearing()); + return {{evaluated.get(), azimuthal}}; +} + +void HillshadeLayerTweaker::execute(LayerGroupBase& layerGroup, + const RenderTree& renderTree, + const PaintParameters& parameters) { + const auto& evaluated = static_cast(*evaluatedProperties).evaluated; + + if (layerGroup.empty()) { + return; + } + +#if !defined(NDEBUG) + const auto label = layerGroup.getName() + "-update-uniforms"; + const auto debugGroup = parameters.encoder->createDebugGroup(label.c_str()); +#endif + + if (!evaluatedPropsUniformBuffer) { + HillshadeEvaluatedPropsUBO evaluatedPropsUBO = {/* .highlight = */ evaluated.get(), + /* .shadow = */ evaluated.get(), + /* .accent = */ evaluated.get()}; + evaluatedPropsUniformBuffer = parameters.context.createUniformBuffer(&evaluatedPropsUBO, + sizeof(evaluatedPropsUBO)); + } + + layerGroup.observeDrawables([&](gfx::Drawable& drawable) { + drawable.mutableUniformBuffers().addOrReplace(HillshadeEvaluatedPropsUBOName, evaluatedPropsUniformBuffer); + + if (!drawable.getTileID()) { + return; + } + const UnwrappedTileID tileID = drawable.getTileID()->toUnwrapped(); + const auto matrix = getTileMatrix( + tileID, renderTree, parameters.state, {0.f, 0.f}, TranslateAnchorType::Viewport, false, false, true); + HillshadeDrawableUBO drawableUBO = {/* .matrix = */ util::cast(matrix), + /* .latrange = */ getLatRange(tileID), + /* .light = */ getLight(parameters, evaluated)}; + + drawable.mutableUniformBuffers().createOrUpdate(HillshadeDrawableUBOName, &drawableUBO, parameters.context); + }); +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/layers/hillshade_layer_tweaker.hpp b/src/mbgl/renderer/layers/hillshade_layer_tweaker.hpp new file mode 100644 index 000000000..03540dac1 --- /dev/null +++ b/src/mbgl/renderer/layers/hillshade_layer_tweaker.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include + +namespace mbgl { + +/** + Hillshade layer specific tweaker + */ +class HillshadeLayerTweaker : public LayerTweaker { +public: + HillshadeLayerTweaker(Immutable properties) + : LayerTweaker(properties){}; + +public: + ~HillshadeLayerTweaker() override = default; + + void execute(LayerGroupBase&, const RenderTree&, const PaintParameters&) override; + +protected: + gfx::UniformBufferPtr evaluatedPropsUniformBuffer; +}; + +} // namespace mbgl diff --git a/src/mbgl/renderer/layers/hillshade_prepare_layer_tweaker.cpp b/src/mbgl/renderer/layers/hillshade_prepare_layer_tweaker.cpp new file mode 100644 index 000000000..2c508e16b --- /dev/null +++ b/src/mbgl/renderer/layers/hillshade_prepare_layer_tweaker.cpp @@ -0,0 +1,73 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mbgl { + +using namespace style; + +struct alignas(16) HillshadePrepareDrawableUBO { + std::array matrix; + std::array unpack; + std::array dimension; + float zoom; + float maxzoom; +}; +static_assert(sizeof(HillshadePrepareDrawableUBO) % 16 == 0); + +static constexpr std::string_view HillshadePrepareDrawableUBOName = "HillshadePrepareDrawableUBO"; + +const std::array& getUnpackVector(Tileset::DEMEncoding encoding) { + // https://www.mapbox.com/help/access-elevation-data/#mapbox-terrain-rgb + static const std::array unpackMapbox = {{6553.6f, 25.6f, 0.1f, 10000.0f}}; + // https://aws.amazon.com/public-datasets/terrain/ + static const std::array unpackTerrarium = {{256.0f, 1.0f, 1.0f / 256.0f, 32768.0f}}; + + return encoding == Tileset::DEMEncoding::Terrarium ? unpackTerrarium : unpackMapbox; +} + +void HillshadePrepareLayerTweaker::execute(LayerGroupBase& layerGroup, + [[maybe_unused]] const RenderTree& renderTree, + const PaintParameters& parameters) { + // const auto& evaluated = static_cast(*evaluatedProperties).evaluated; + + if (layerGroup.empty()) { + return; + } + +#if !defined(NDEBUG) + const auto label = layerGroup.getName() + "-update-uniforms"; + const auto debugGroup = parameters.encoder->createDebugGroup(label.c_str()); +#endif + + layerGroup.observeDrawables([&](gfx::Drawable& drawable) { + if (!drawable.getTileID() || !drawable.getData()) { + return; + } + const UnwrappedTileID tileID = drawable.getTileID()->toUnwrapped(); + const auto& drawableData = static_cast(*drawable.getData()); + + mat4 matrix; + matrix::ortho(matrix, 0, util::EXTENT, -util::EXTENT, 0, 0, 1); + matrix::translate(matrix, matrix, 0, -util::EXTENT, 0); + + HillshadePrepareDrawableUBO drawableUBO = { + /* .matrix = */ util::cast(matrix), + /* .unpack = */ getUnpackVector(drawableData.encoding), + /* .dimension = */ {static_cast(drawableData.stride), static_cast(drawableData.stride)}, + /* .zoom = */ static_cast(tileID.canonical.z), + /* .maxzoom = */ static_cast(drawableData.maxzoom)}; + + drawable.mutableUniformBuffers().createOrUpdate( + HillshadePrepareDrawableUBOName, &drawableUBO, parameters.context); + }); +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/layers/hillshade_prepare_layer_tweaker.hpp b/src/mbgl/renderer/layers/hillshade_prepare_layer_tweaker.hpp new file mode 100644 index 000000000..e7e220f5e --- /dev/null +++ b/src/mbgl/renderer/layers/hillshade_prepare_layer_tweaker.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include + +namespace mbgl { + +/** + Hillshade prepare layer specific tweaker + */ +class HillshadePrepareLayerTweaker : public LayerTweaker { +public: + HillshadePrepareLayerTweaker(Immutable properties) + : LayerTweaker(properties){}; + +public: + ~HillshadePrepareLayerTweaker() override = default; + + void execute(LayerGroupBase&, const RenderTree&, const PaintParameters&) override; + +protected: +}; + +} // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_heatmap_layer.cpp b/src/mbgl/renderer/layers/render_heatmap_layer.cpp index 15237a818..0d3b324d9 100644 --- a/src/mbgl/renderer/layers/render_heatmap_layer.cpp +++ b/src/mbgl/renderer/layers/render_heatmap_layer.cpp @@ -237,6 +237,7 @@ bool RenderHeatmapLayer::queryIntersectsFeature(const GeometryCoordinates& query } #if MLN_DRAWABLE_RENDERER +namespace { void activateRenderTarget(const RenderTargetPtr& renderTarget_, bool activate, UniqueChangeRequestVec& changes) { if (renderTarget_) { if (activate) { @@ -248,6 +249,7 @@ void activateRenderTarget(const RenderTargetPtr& renderTarget_, bool activate, U } } } +} // namespace void RenderHeatmapLayer::markLayerRenderable(bool willRender, UniqueChangeRequestVec& changes) { RenderLayer::markLayerRenderable(willRender, changes); @@ -287,7 +289,7 @@ void RenderHeatmapLayer::update(gfx::ShaderRegistry& shaders, gfx::Context& context, const TransformState& state, [[maybe_unused]] const RenderTree& renderTree, - [[maybe_unused]] UniqueChangeRequestVec& changes) { + UniqueChangeRequestVec& changes) { std::unique_lock guard(mutex); if (!renderTiles || renderTiles->empty()) { diff --git a/src/mbgl/renderer/layers/render_hillshade_layer.cpp b/src/mbgl/renderer/layers/render_hillshade_layer.cpp index f5ae3796c..e44c34162 100644 --- a/src/mbgl/renderer/layers/render_hillshade_layer.cpp +++ b/src/mbgl/renderer/layers/render_hillshade_layer.cpp @@ -5,8 +5,6 @@ #include #include #include -#include -#include #include #include #include @@ -15,6 +13,19 @@ #include #include +#if MLN_DRAWABLE_RENDERER +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + namespace mbgl { using namespace style; @@ -60,6 +71,11 @@ void RenderHillshadeLayer::evaluate(const PropertyEvaluationParameters& paramete : RenderPass::None; properties->renderPasses = mbgl::underlying_type(passes); evaluatedProperties = std::move(properties); +#if MLN_DRAWABLE_RENDERER + if (layerGroup) { + layerGroup->setLayerTweaker(std::make_shared(evaluatedProperties)); + } +#endif } bool RenderHillshadeLayer::hasTransition() const { @@ -73,6 +89,10 @@ bool RenderHillshadeLayer::hasCrossfade() const { void RenderHillshadeLayer::prepare(const LayerPrepareParameters& params) { renderTiles = params.source->getRenderTiles(); maxzoom = params.source->getMaxZoom(); + +#if MLN_DRAWABLE_RENDERER + updateRenderTileIDs(); +#endif // MLN_DRAWABLE_RENDERER } #if MLN_LEGACY_RENDERER @@ -221,4 +241,267 @@ void RenderHillshadeLayer::render(PaintParameters& parameters) { } #endif // MLN_LEGACY_RENDERER +#if MLN_DRAWABLE_RENDERER +namespace { +void activateRenderTarget(const RenderTargetPtr& renderTarget_, bool activate, UniqueChangeRequestVec& changes) { + if (renderTarget_) { + if (activate) { + // The RenderTree has determined this render target should be included in the renderable set for a frame + changes.emplace_back(std::make_unique(renderTarget_)); + } else { + // The RenderTree is informing us we should not render anything + changes.emplace_back(std::make_unique(renderTarget_)); + } + } +} +} // namespace + +void RenderHillshadeLayer::markLayerRenderable(bool willRender, UniqueChangeRequestVec& changes) { + RenderLayer::markLayerRenderable(willRender, changes); + removeRenderTargets(changes); +} + +void RenderHillshadeLayer::layerRemoved(UniqueChangeRequestVec& changes) { + RenderLayer::layerRemoved(changes); + removeRenderTargets(changes); +} + +void RenderHillshadeLayer::addRenderTarget(const RenderTargetPtr& renderTarget, UniqueChangeRequestVec& changes) { + activateRenderTarget(renderTarget, true, changes); + activatedRenderTargets.emplace_back(renderTarget); +} + +void RenderHillshadeLayer::removeRenderTargets(UniqueChangeRequestVec& changes) { + for (const auto& renderTarget : activatedRenderTargets) { + activateRenderTarget(renderTarget, false, changes); + } + activatedRenderTargets.clear(); +} + +static const std::string HillshadePrepareShaderGroupName = "HillshadePrepareShader"; +static const std::string HillshadeShaderGroupName = "HillshadeShader"; + +constexpr auto PosAttribName = "a_pos"; +constexpr auto TexturePosAttribName = "a_texture_pos"; + +void RenderHillshadeLayer::update(gfx::ShaderRegistry& shaders, + gfx::Context& context, + [[maybe_unused]] const TransformState& state, + [[maybe_unused]] const RenderTree& renderTree, + UniqueChangeRequestVec& changes) { + std::unique_lock guard(mutex); + + if (!renderTiles || renderTiles->empty()) { + removeAllDrawables(); + return; + } + + // Set up a layer group + if (!layerGroup) { + auto layerGroup_ = context.createTileLayerGroup(layerIndex, /*initialCapacity=*/64, getID()); + if (!layerGroup_) { + return; + } + layerGroup_->setLayerTweaker(std::make_shared(evaluatedProperties)); + setLayerGroup(std::move(layerGroup_), changes); + } + + auto* tileLayerGroup = static_cast(layerGroup.get()); + + if (!hillshadePrepareShader) { + hillshadePrepareShader = context.getGenericShader(shaders, HillshadePrepareShaderGroupName); + } + if (!hillshadeShader) { + hillshadeShader = context.getGenericShader(shaders, HillshadeShaderGroupName); + } + if (!hillshadePrepareShader || !hillshadeShader) { + removeAllDrawables(); + return; + } + + auto renderPass = RenderPass::Translucent; + if (!(mbgl::underlying_type(renderPass) & evaluatedProperties->renderPasses)) { + return; + } + + stats.drawablesRemoved += tileLayerGroup->observeDrawablesRemove([&](gfx::Drawable& drawable) { + return (!drawable.getTileID() || renderTileIDs.find(*drawable.getTileID()) != renderTileIDs.end()); + }); + + if (!staticDataSharedVertices) { + staticDataSharedVertices = std::make_shared(RenderStaticData::rasterVertices()); + } + const auto staticDataIndices = RenderStaticData::quadTriangleIndices(); + const auto staticDataSegments = RenderStaticData::rasterSegments(); + + std::unique_ptr hillshadeBuilder; + std::unique_ptr hillshadePrepareBuilder; + + for (const RenderTile& tile : *renderTiles) { + const auto& tileID = tile.getOverscaledTileID(); + + auto* bucket_ = tile.getBucket(*baseImpl); + if (!bucket_) { + removeTile(renderPass, tileID); + continue; + } + auto& bucket = static_cast(*bucket_); + + if (!bucket.hasData()) { + continue; + } + + if (!bucket.renderTargetPrepared) { + // Set up tile render target + const uint16_t tilesize = bucket.getDEMData().dim; + auto renderTarget = context.createRenderTarget({tilesize, tilesize}, + gfx::TextureChannelDataType::UnsignedByte); + if (!renderTarget) { + continue; + } + bucket.renderTarget = renderTarget; + bucket.renderTargetPrepared = true; + addRenderTarget(renderTarget, changes); + + auto singleTileLayerGroup = context.createTileLayerGroup(0, /*initialCapacity=*/1, getID()); + if (!singleTileLayerGroup) { + return; + } + singleTileLayerGroup->setLayerTweaker(std::make_shared(evaluatedProperties)); + renderTarget->addLayerGroup(singleTileLayerGroup, /*replace=*/true); + + gfx::VertexAttributeArray hillshadePrepareVertexAttrs; + + if (const auto& attr = hillshadePrepareVertexAttrs.add(PosAttribName)) { + attr->setSharedRawData(staticDataSharedVertices, + offsetof(HillshadeLayoutVertex, a1), + 0, + sizeof(HillshadeLayoutVertex), + gfx::AttributeDataType::Short2); + } + if (const auto& attr = hillshadePrepareVertexAttrs.getOrAdd(TexturePosAttribName)) { + attr->setSharedRawData(staticDataSharedVertices, + offsetof(HillshadeLayoutVertex, a2), + 0, + sizeof(HillshadeLayoutVertex), + gfx::AttributeDataType::Short4); + } + + hillshadePrepareBuilder = context.createDrawableBuilder("hillshadePrepare"); + hillshadePrepareBuilder->setShader(hillshadePrepareShader); + hillshadePrepareBuilder->setDepthType(gfx::DepthMaskType::ReadOnly); + hillshadePrepareBuilder->setColorMode(gfx::ColorMode::alphaBlended()); + hillshadePrepareBuilder->setCullFaceMode(gfx::CullFaceMode::disabled()); + + hillshadePrepareBuilder->setRenderPass(renderPass); + hillshadePrepareBuilder->setVertexAttributes(std::move(hillshadePrepareVertexAttrs)); + hillshadePrepareBuilder->setRawVertices( + {}, staticDataSharedVertices->elements(), gfx::AttributeDataType::Short2); + hillshadePrepareBuilder->setSegments( + gfx::Triangles(), staticDataIndices.vector(), staticDataSegments.data(), staticDataSegments.size()); + + auto imageLocation = hillshadePrepareShader->getSamplerLocation("u_image"); + if (imageLocation.has_value()) { + std::shared_ptr texture = context.createTexture2D(); + texture->setImage(bucket.getDEMData().getImagePtr()); + texture->setSamplerConfiguration( + {gfx::TextureFilterType::Linear, gfx::TextureWrapType::Clamp, gfx::TextureWrapType::Clamp}); + hillshadePrepareBuilder->setTexture(texture, imageLocation.value()); + } + + hillshadePrepareBuilder->flush(); + + for (auto& drawable : hillshadePrepareBuilder->clearDrawables()) { + drawable->setTileID(tileID); + drawable->setData(std::make_unique( + bucket.getDEMData().stride, bucket.getDEMData().encoding, maxzoom)); + singleTileLayerGroup->addDrawable(renderPass, tileID, std::move(drawable)); + ++stats.drawablesAdded; + } + } + + // Set up tile drawable + auto vertices = staticDataSharedVertices; + auto* indices = &staticDataIndices; + auto* segments = &staticDataSegments; + + if (!bucket.vertices.empty() && !bucket.indices.empty() && !bucket.segments.empty()) { + vertices = bucket.sharedVertices; + indices = &bucket.indices; + segments = &bucket.segments; + } + + gfx::VertexAttributeArray hillshadeVertexAttrs; + + if (const auto& attr = hillshadeVertexAttrs.add(PosAttribName)) { + attr->setSharedRawData(vertices, + offsetof(HillshadeLayoutVertex, a1), + 0, + sizeof(HillshadeLayoutVertex), + gfx::AttributeDataType::Short2); + } + if (const auto& attr = hillshadeVertexAttrs.getOrAdd(TexturePosAttribName)) { + attr->setSharedRawData(vertices, + offsetof(HillshadeLayoutVertex, a2), + 0, + sizeof(HillshadeLayoutVertex), + gfx::AttributeDataType::Short4); + } + + hillshadeBuilder = context.createDrawableBuilder("hillshade"); + + if (tileLayerGroup->getDrawableCount(renderPass, tileID) > 0) { + tileLayerGroup->observeDrawables(renderPass, tileID, [&](gfx::Drawable& drawable) { + drawable.setVertexAttributes(std::move(hillshadeVertexAttrs)); + drawable.setVertices({}, vertices->elements(), gfx::AttributeDataType::Short2); + + std::vector> drawSegments; + for (std::size_t i = 0; i < segments->size(); ++i) { + const auto& seg = segments->data()[i]; + auto segCopy = SegmentBase{ + // no copy constructor + seg.vertexOffset, + seg.indexOffset, + seg.vertexLength, + seg.indexLength, + seg.sortKey, + }; + drawSegments.emplace_back(hillshadeBuilder->createSegment(gfx::Triangles(), std::move(segCopy))); + } + drawable.setIndexData(std::move(indices->vector()), std::move(drawSegments)); + + auto imageLocation = hillshadeShader->getSamplerLocation("u_image"); + if (imageLocation.has_value()) { + drawable.setTexture(bucket.renderTarget->getTexture(), imageLocation.value()); + } + }); + continue; + } + + hillshadeBuilder->setShader(hillshadeShader); + hillshadeBuilder->setDepthType(gfx::DepthMaskType::ReadOnly); + hillshadeBuilder->setColorMode(gfx::ColorMode::alphaBlended()); + hillshadeBuilder->setCullFaceMode(gfx::CullFaceMode::disabled()); + + hillshadeBuilder->setRenderPass(renderPass); + hillshadeBuilder->setVertexAttributes(std::move(hillshadeVertexAttrs)); + hillshadeBuilder->setRawVertices({}, vertices->elements(), gfx::AttributeDataType::Short2); + hillshadeBuilder->setSegments(gfx::Triangles(), indices->vector(), segments->data(), segments->size()); + + auto imageLocation = hillshadeShader->getSamplerLocation("u_image"); + if (imageLocation.has_value()) { + hillshadeBuilder->setTexture(bucket.renderTarget->getTexture(), imageLocation.value()); + } + + hillshadeBuilder->flush(); + + for (auto& drawable : hillshadeBuilder->clearDrawables()) { + drawable->setTileID(tileID); + tileLayerGroup->addDrawable(renderPass, tileID, std::move(drawable)); + ++stats.drawablesAdded; + } + } +} +#endif // MLN_DRAWABLE_RENDERER + } // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_hillshade_layer.hpp b/src/mbgl/renderer/layers/render_hillshade_layer.hpp index 924379565..b1f0cb747 100644 --- a/src/mbgl/renderer/layers/render_hillshade_layer.hpp +++ b/src/mbgl/renderer/layers/render_hillshade_layer.hpp @@ -1,6 +1,8 @@ #pragma once #include +#include +#include #include #include #include @@ -15,6 +17,19 @@ public: explicit RenderHillshadeLayer(Immutable); ~RenderHillshadeLayer() override; +#if MLN_DRAWABLE_RENDERER + void markLayerRenderable(bool willRender, UniqueChangeRequestVec& changes) override; + + void layerRemoved(UniqueChangeRequestVec&) override; + + /// Generate any changes needed by the layer + void update(gfx::ShaderRegistry&, + gfx::Context&, + const TransformState&, + const RenderTree&, + UniqueChangeRequestVec&) override; +#endif + private: void transition(const TransitionParameters&) override; void evaluate(const PropertyEvaluationParameters&) override; @@ -27,6 +42,11 @@ private: void prepare(const LayerPrepareParameters&) override; +#if MLN_DRAWABLE_RENDERER + void addRenderTarget(const RenderTargetPtr&, UniqueChangeRequestVec&); + void removeRenderTargets(UniqueChangeRequestVec&); +#endif + // Paint properties style::HillshadePaintProperties::Unevaluated unevaluated; uint8_t maxzoom = util::TERRAIN_RGB_MAXZOOM; @@ -39,6 +59,15 @@ private: std::shared_ptr hillshadeProgram; std::shared_ptr hillshadePrepareProgram; #endif + +#if MLN_DRAWABLE_RENDERER + gfx::ShaderProgramBasePtr hillshadePrepareShader; + gfx::ShaderProgramBasePtr hillshadeShader; + std::vector activatedRenderTargets; + + using HillshadeVertexVector = gfx::VertexVector; + std::shared_ptr staticDataSharedVertices; +#endif }; } // namespace mbgl diff --git a/src/mbgl/tile/raster_dem_tile.cpp b/src/mbgl/tile/raster_dem_tile.cpp index c116fedaf..bbdaaeab8 100644 --- a/src/mbgl/tile/raster_dem_tile.cpp +++ b/src/mbgl/tile/raster_dem_tile.cpp @@ -105,6 +105,9 @@ void RasterDEMTile::backfillBorder(const RasterDEMTile& borderTile, const DEMTil // mark HillshadeBucket.prepared as false so it runs through the prepare // render pass with the new texture data we just backfilled bucket->setPrepared(false); +#if MLN_DRAWABLE_RENDERER + bucket->renderTargetPrepared = false; +#endif } }