Hillshade layer (#1305)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Tim Sylvester <tim@wetdogweather.com>
Co-authored-by: Alex Cristici <alex.cristici@gmail.com>
This commit is contained in:
Alex Cristici
2023-07-12 19:45:30 +03:00
committed by Bart Louwers
parent 95f0cbed33
commit 3056f28756
34 changed files with 1017 additions and 40 deletions

View File

@@ -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

View File

@@ -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",

View File

@@ -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<uint8_t>&&, std::size_t, AttributeDataType) = 0;
/// Create a segment wrapper
virtual void setIndexData(std::vector<std::uint16_t> indexes, std::vector<UniqueDrawSegment> segments) = 0;
/// Get the tweakers attached to this drawable
const std::vector<DrawableTweakerPtr>& getTweakers() const { return tweakers; }
void addTweaker(DrawableTweakerPtr value) { tweakers.emplace_back(std::move(value)); }

View File

@@ -152,15 +152,15 @@ public:
std::size_t indexLength,
std::size_t baseIndex = 0);
/// Create a segment wrapper
virtual std::unique_ptr<Drawable::DrawSegment> 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<Drawable::DrawSegment> createSegment(gfx::DrawMode, SegmentBase&&) = 0;
/// Setup the SDK-specific aspects after all the values are present
virtual void init() = 0;

View File

@@ -36,9 +36,9 @@ public:
void draw(PaintParameters&) const override;
struct DrawSegmentGL;
void setIndexData(std::vector<std::uint16_t> indexes, std::vector<UniqueDrawSegment> segments);
void setIndexData(std::vector<std::uint16_t> indexes, std::vector<UniqueDrawSegment> segments) override;
void setVertices(std::vector<uint8_t>&&, std::size_t, gfx::AttributeDataType);
void setVertices(std::vector<uint8_t>&&, std::size_t, gfx::AttributeDataType) override;
const gfx::VertexAttributeArray& getVertexAttributes() const override;
void setVertexAttributes(const gfx::VertexAttributeArray& value) override;

View File

@@ -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<DrawSegment> 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;
};

View File

@@ -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

View File

@@ -45,7 +45,8 @@ protected:
const std::array<float, 2>& translation,
style::TranslateAnchorType,
bool nearClipped,
bool inViewportPixelUnits);
bool inViewportPixelUnits,
bool aligned = false);
protected:
Immutable<style::LayerProperties> evaluatedProperties;

View File

@@ -0,0 +1,89 @@
// Generated code, do not modify this file!
#pragma once
#include <mbgl/shaders/shader_source.hpp>
namespace mbgl {
namespace shaders {
template <>
struct ShaderSource<BuiltIn::HillshadeShader, gfx::Backend::Type::OpenGL> {
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

View File

@@ -0,0 +1,114 @@
// Generated code, do not modify this file!
#pragma once
#include <mbgl/shaders/shader_source.hpp>
namespace mbgl {
namespace shaders {
template <>
struct ShaderSource<BuiltIn::HillshadePrepareShader, gfx::Backend::Type::OpenGL> {
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

View File

@@ -18,6 +18,8 @@
#include <mbgl/shaders/gl/drawable_fill_extrusion_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_raster.hpp>
#include <mbgl/shaders/gl/drawable_symbol_icon.hpp>
#include <mbgl/shaders/gl/drawable_symbol_sdf_text.hpp>

View File

@@ -24,6 +24,8 @@ enum class BuiltIn {
FillExtrusionPatternShader,
HeatmapShader,
HeatmapTextureShader,
HillshadePrepareShader,
HillshadeShader,
RasterShader,
SymbolIconShader,
SymbolSDFTextShader,

View File

@@ -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
}

View File

@@ -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;
}

View File

@@ -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
}

View File

@@ -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;
}

View File

@@ -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",

View File

@@ -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<uint32_t>(stride), static_cast<uint32_t>(stride)}) {
encoding(_encoding) {
image = std::make_shared<PremultipliedImage>(Size(static_cast<uint32_t>(stride), static_cast<uint32_t>(stride)));
if (_image.size.height != _image.size.width) {
throw std::runtime_error("raster-dem tiles must be square.");
}
auto* dest = reinterpret_cast<uint32_t*>(image.data.get()) + stride + 1;
auto* dest = reinterpret_cast<uint32_t*>(image->data.get()) + stride + 1;
auto* source = reinterpret_cast<uint32_t*>(_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<uint32_t*>(image.data.get());
auto* data = reinterpret_cast<uint32_t*>(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<uint32_t*>(image.data.get());
auto* source = reinterpret_cast<uint32_t*>(o.image.data.get());
auto* dest = reinterpret_cast<uint32_t*>(image->data.get());
auto* source = reinterpret_cast<uint32_t*>(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<int32_t>(value[0] * unpack[0] + value[1] * unpack[1] + value[2] * unpack[2] - unpack[3]);
}

View File

@@ -19,14 +19,15 @@ public:
int32_t get(int32_t x, int32_t y) const;
const std::array<float, 4>& getUnpackVector() const;
const PremultipliedImage* getImage() const { return &image; }
const PremultipliedImage* getImage() const { return &*image; }
const std::shared_ptr<PremultipliedImage>& 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<PremultipliedImage> image;
size_t idx(const int32_t x, const int32_t y) const {
assert(x >= -1);

View File

@@ -0,0 +1,27 @@
#pragma once
#include <mbgl/gfx/drawable_data.hpp>
#include <mbgl/util/tileset.hpp>
#include <memory>
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<HillshadePrepareDrawableData>;
} // namespace gfx
} // namespace mbgl

View File

@@ -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,

View File

@@ -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;

View File

@@ -30,6 +30,11 @@ public:
std::optional<gfx::Texture> dem;
std::optional<gfx::Texture> 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<HillshadeLayoutVertex> vertices;
using VertexVector = gfx::VertexVector<HillshadeLayoutVertex>;
std::shared_ptr<VertexVector> sharedVertices = std::make_shared<VertexVector>();
VertexVector& vertices = *sharedVertices;
gfx::IndexVector<gfx::Triangles> indices;
SegmentVector<HillshadeAttributes> segments;

View File

@@ -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);

View File

@@ -17,14 +17,17 @@ mat4 LayerTweaker::getTileMatrix(const UnwrappedTileID& tileID,
const std::array<float, 2>& 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);

View File

@@ -18,7 +18,6 @@ public:
void execute(LayerGroupBase&, const RenderTree&, const PaintParameters&) override;
protected:
gfx::UniformBufferPtr paintParamsUniformBuffer;
gfx::UniformBufferPtr evaluatedPropsUniformBuffer;
};

View File

@@ -0,0 +1,85 @@
#include <mbgl/renderer/layers/hillshade_layer_tweaker.hpp>
#include <mbgl/gfx/context.hpp>
#include <mbgl/gfx/drawable.hpp>
#include <mbgl/renderer/layer_group.hpp>
#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/renderer/render_tree.hpp>
#include <mbgl/style/layers/hillshade_layer_properties.hpp>
#include <mbgl/util/convert.hpp>
namespace mbgl {
using namespace style;
struct alignas(16) HillshadeDrawableUBO {
std::array<float, 4 * 4> matrix;
std::array<float, 2> latrange;
std::array<float, 2> 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<float, 2> 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<float>(latlng0.latitude()), static_cast<float>(latlng1.latitude())}};
}
std::array<float, 2> getLight(const PaintParameters& parameters,
const HillshadePaintProperties::PossiblyEvaluated& evaluated) {
float azimuthal = util::deg2radf(evaluated.get<HillshadeIlluminationDirection>());
if (evaluated.get<HillshadeIlluminationAnchor>() == HillshadeIlluminationAnchorType::Viewport)
azimuthal = azimuthal - static_cast<float>(parameters.state.getBearing());
return {{evaluated.get<HillshadeExaggeration>(), azimuthal}};
}
void HillshadeLayerTweaker::execute(LayerGroupBase& layerGroup,
const RenderTree& renderTree,
const PaintParameters& parameters) {
const auto& evaluated = static_cast<const HillshadeLayerProperties&>(*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<HillshadeHighlightColor>(),
/* .shadow = */ evaluated.get<HillshadeShadowColor>(),
/* .accent = */ evaluated.get<HillshadeAccentColor>()};
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<float>(matrix),
/* .latrange = */ getLatRange(tileID),
/* .light = */ getLight(parameters, evaluated)};
drawable.mutableUniformBuffers().createOrUpdate(HillshadeDrawableUBOName, &drawableUBO, parameters.context);
});
}
} // namespace mbgl

View File

@@ -0,0 +1,24 @@
#pragma once
#include <mbgl/renderer/layer_tweaker.hpp>
namespace mbgl {
/**
Hillshade layer specific tweaker
*/
class HillshadeLayerTweaker : public LayerTweaker {
public:
HillshadeLayerTweaker(Immutable<style::LayerProperties> properties)
: LayerTweaker(properties){};
public:
~HillshadeLayerTweaker() override = default;
void execute(LayerGroupBase&, const RenderTree&, const PaintParameters&) override;
protected:
gfx::UniformBufferPtr evaluatedPropsUniformBuffer;
};
} // namespace mbgl

View File

@@ -0,0 +1,73 @@
#include <mbgl/renderer/layers/hillshade_prepare_layer_tweaker.hpp>
#include <mbgl/gfx/context.hpp>
#include <mbgl/gfx/drawable.hpp>
#include <mbgl/gfx/hillshade_prepare_drawable_data.hpp>
#include <mbgl/renderer/layer_group.hpp>
#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/renderer/render_tree.hpp>
#include <mbgl/style/layers/hillshade_layer_properties.hpp>
#include <mbgl/util/convert.hpp>
namespace mbgl {
using namespace style;
struct alignas(16) HillshadePrepareDrawableUBO {
std::array<float, 4 * 4> matrix;
std::array<float, 4> unpack;
std::array<float, 2> dimension;
float zoom;
float maxzoom;
};
static_assert(sizeof(HillshadePrepareDrawableUBO) % 16 == 0);
static constexpr std::string_view HillshadePrepareDrawableUBOName = "HillshadePrepareDrawableUBO";
const std::array<float, 4>& getUnpackVector(Tileset::DEMEncoding encoding) {
// https://www.mapbox.com/help/access-elevation-data/#mapbox-terrain-rgb
static const std::array<float, 4> unpackMapbox = {{6553.6f, 25.6f, 0.1f, 10000.0f}};
// https://aws.amazon.com/public-datasets/terrain/
static const std::array<float, 4> 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<const HillshadeLayerProperties&>(*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<const gfx::HillshadePrepareDrawableData&>(*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<float>(matrix),
/* .unpack = */ getUnpackVector(drawableData.encoding),
/* .dimension = */ {static_cast<float>(drawableData.stride), static_cast<float>(drawableData.stride)},
/* .zoom = */ static_cast<float>(tileID.canonical.z),
/* .maxzoom = */ static_cast<float>(drawableData.maxzoom)};
drawable.mutableUniformBuffers().createOrUpdate(
HillshadePrepareDrawableUBOName, &drawableUBO, parameters.context);
});
}
} // namespace mbgl

View File

@@ -0,0 +1,23 @@
#pragma once
#include <mbgl/renderer/layer_tweaker.hpp>
namespace mbgl {
/**
Hillshade prepare layer specific tweaker
*/
class HillshadePrepareLayerTweaker : public LayerTweaker {
public:
HillshadePrepareLayerTweaker(Immutable<style::LayerProperties> properties)
: LayerTweaker(properties){};
public:
~HillshadePrepareLayerTweaker() override = default;
void execute(LayerGroupBase&, const RenderTree&, const PaintParameters&) override;
protected:
};
} // namespace mbgl

View File

@@ -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<std::mutex> guard(mutex);
if (!renderTiles || renderTiles->empty()) {

View File

@@ -5,8 +5,6 @@
#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/renderer/render_static_data.hpp>
#include <mbgl/programs/programs.hpp>
#include <mbgl/programs/hillshade_program.hpp>
#include <mbgl/programs/hillshade_prepare_program.hpp>
#include <mbgl/tile/tile.hpp>
#include <mbgl/style/layers/hillshade_layer_impl.hpp>
#include <mbgl/gfx/cull_face_mode.hpp>
@@ -15,6 +13,19 @@
#include <mbgl/math/angles.hpp>
#include <mbgl/util/geo.hpp>
#if MLN_DRAWABLE_RENDERER
#include <mbgl/renderer/layers/hillshade_layer_tweaker.hpp>
#include <mbgl/renderer/layers/hillshade_prepare_layer_tweaker.hpp>
#include <mbgl/renderer/layer_group.hpp>
#include <mbgl/renderer/render_target.hpp>
#include <mbgl/shaders/shader_program_base.hpp>
#include <mbgl/gfx/drawable_builder.hpp>
#include <mbgl/gfx/drawable_impl.hpp>
#include <mbgl/gfx/hillshade_prepare_drawable_data.hpp>
#include <mbgl/gfx/shader_group.hpp>
#include <mbgl/gfx/shader_registry.hpp>
#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<HillshadeLayerTweaker>(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<AddRenderTargetRequest>(renderTarget_));
} else {
// The RenderTree is informing us we should not render anything
changes.emplace_back(std::make_unique<RemoveRenderTargetRequest>(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<std::mutex> 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<HillshadeLayerTweaker>(evaluatedProperties));
setLayerGroup(std::move(layerGroup_), changes);
}
auto* tileLayerGroup = static_cast<TileLayerGroup*>(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<HillshadeVertexVector>(RenderStaticData::rasterVertices());
}
const auto staticDataIndices = RenderStaticData::quadTriangleIndices();
const auto staticDataSegments = RenderStaticData::rasterSegments();
std::unique_ptr<gfx::DrawableBuilder> hillshadeBuilder;
std::unique_ptr<gfx::DrawableBuilder> 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<HillshadeBucket&>(*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<HillshadePrepareLayerTweaker>(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<gfx::Texture2D> 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<gfx::HillshadePrepareDrawableData>(
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<std::unique_ptr<gfx::Drawable::DrawSegment>> 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

View File

@@ -1,6 +1,8 @@
#pragma once
#include <mbgl/renderer/render_layer.hpp>
#include <mbgl/programs/hillshade_program.hpp>
#include <mbgl/programs/hillshade_prepare_program.hpp>
#include <mbgl/style/layers/hillshade_layer_impl.hpp>
#include <mbgl/style/layers/hillshade_layer_properties.hpp>
#include <mbgl/tile/tile_id.hpp>
@@ -15,6 +17,19 @@ public:
explicit RenderHillshadeLayer(Immutable<style::HillshadeLayer::Impl>);
~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> hillshadeProgram;
std::shared_ptr<HillshadePrepareProgram> hillshadePrepareProgram;
#endif
#if MLN_DRAWABLE_RENDERER
gfx::ShaderProgramBasePtr hillshadePrepareShader;
gfx::ShaderProgramBasePtr hillshadeShader;
std::vector<RenderTargetPtr> activatedRenderTargets;
using HillshadeVertexVector = gfx::VertexVector<HillshadeLayoutVertex>;
std::shared_ptr<HillshadeVertexVector> staticDataSharedVertices;
#endif
};
} // namespace mbgl

View File

@@ -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
}
}