Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Commit

Permalink
[core] better symbol fading with texture lookups (#4579)
Browse files Browse the repository at this point in the history
- this is simpler than predicting opacity based on current zooming speed
- this is smoother: symbols don't flicker when changing zoom speed or
  when zooming in and out rapidly.

mapbox/mapbox-gl-js@1df1466

fix #4562
  • Loading branch information
ansis authored and jfirebaugh committed May 20, 2016
1 parent 99e2504 commit 4c62a55
Show file tree
Hide file tree
Showing 10 changed files with 135 additions and 162 deletions.
150 changes: 89 additions & 61 deletions src/mbgl/renderer/frame_history.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,82 +3,110 @@

using namespace mbgl;

// Record frame history that will be used to calculate fading params
void FrameHistory::record(const TimePoint& now, float zoom) {
// first frame ever
if (history.empty()) {
history.emplace_back(FrameSnapshot{TimePoint::min(), zoom});
history.emplace_back(FrameSnapshot{TimePoint::min(), zoom});
}
FrameHistory::FrameHistory() {
changeTimes.fill(TimePoint::min());
changeOpacities.fill(0);
opacities.fill(0);
};

if (!history.empty() || history.back().z != zoom) {
history.emplace_back(FrameSnapshot{now, zoom});
}
}
void FrameHistory::record(const TimePoint& now, float zoom, const Duration& duration) {

bool FrameHistory::needsAnimation(const Duration& duration) const {
if (history.empty()) {
return false;
}
int16_t zoomIndex = std::floor(zoom * 10.0);

// If we have a value that is older than duration and whose z value is the
// same as the most current z value, and if all values inbetween have the
// same z value, we don't need animation, otherwise we probably do.
const FrameSnapshot& pivot = history.back();
if (firstFrame) {

int i = -1;
while ((int)history.size() > i + 1 && history[i + 1].now + duration < pivot.now) {
++i;
for (int16_t z = 0; z <= zoomIndex; z++) {
opacities[z] = 255u;
}
firstFrame = false;
}

if (i < 0) {
// There is no frame that is older than the duration time, so we need to
// check all frames.
i = 0;
if (zoomIndex < previousZoomIndex) {
for (int16_t z = zoomIndex + 1; z <= previousZoomIndex; z++) {
changeTimes[z] = now;
changeOpacities[z] = opacities[z];
}
} else {
for (int16_t z = zoomIndex; z > previousZoomIndex; z--) {
changeTimes[z] = now;
changeOpacities[z] = opacities[z];
}
}

// Make sure that all subsequent snapshots have the same zoom as the last
// pivot element.
for (; (int)history.size() > i; ++i) {
if (history[i].z != pivot.z) {
return true;
for (int16_t z = 0; z <= 255; z++) {
std::chrono::duration<float> timeDiff = now - changeTimes[z];
int32_t opacityChange = (duration == Milliseconds(0) ? 1 : (timeDiff / duration)) * 255;
if (z <= zoomIndex) {
opacities[z] = util::min(255, changeOpacities[z] + opacityChange);
} else {
opacities[z] = util::max(0, changeOpacities[z] - opacityChange);
}
}

return false;
changed = true;

if (zoomIndex != previousZoomIndex) {
previousZoomIndex = zoomIndex;
previousTime = now;
}

time = now;
}

FadeProperties FrameHistory::getFadeProperties(const TimePoint& now, const Duration& duration) {
// Remove frames until only one is outside the duration, or until there are only three
while (history.size() > 3 && history[1].now + duration < now) {
history.pop_front();
bool FrameHistory::needsAnimation(const Duration& duration) const {
return (time - previousTime) < duration;
}

void FrameHistory::upload(gl::GLObjectStore& glObjectStore) {

if (changed) {
const bool first = !texture;
bind(glObjectStore);

if (first) {
MBGL_CHECK_ERROR(glTexImage2D(
GL_TEXTURE_2D, // GLenum target
0, // GLint level
GL_ALPHA, // GLint internalformat
width, // GLsizei width
height, // GLsizei height
0, // GLint border
GL_ALPHA, // GLenum format
GL_UNSIGNED_BYTE, // GLenum type
opacities.data()
));
} else {
MBGL_CHECK_ERROR(glTexSubImage2D(
GL_TEXTURE_2D, // GLenum target
0, // GLint level
0, // GLint xoffset
0, // GLint yoffset
width, // GLsizei width
height, // GLsizei height
GL_ALPHA, // GLenum format
GL_UNSIGNED_BYTE, // GLenum type
opacities.data()
));
}

changed = false;

}
}

if (history[1].now + duration < now) {
history[0].z = history[1].z;
void FrameHistory::bind(gl::GLObjectStore& glObjectStore) {
if (!texture) {
texture.create(glObjectStore);
MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture.getID()));
#ifndef GL_ES_VERSION_2_0
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
#endif
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
} else {
MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture.getID()));
}

// Find the range of zoom levels we want to fade between
float startingZ = history.front().z;
const FrameSnapshot lastFrame = history.back();
float endingZ = lastFrame.z;
float lowZ = util::min(startingZ, endingZ);
float highZ = util::max(startingZ, endingZ);

// Calculate the speed of zooming, and how far it would zoom in terms of zoom levels in one
// duration
float zoomDiff = endingZ - history[1].z;
std::chrono::duration<float> timeDiff = lastFrame.now - history[1].now;
float fadedist = zoomDiff / (timeDiff / duration);

// At end of a zoom when the zoom stops changing continue pretending to zoom at that speed
// bump is how much farther it would have been if it had continued zooming at the same rate
float bump = std::chrono::duration<float>(now - lastFrame.now) / duration * fadedist;

return FadeProperties {
fadedist,
lowZ,
highZ,
bump
};
}
39 changes: 20 additions & 19 deletions src/mbgl/renderer/frame_history.hpp
Original file line number Diff line number Diff line change
@@ -1,37 +1,38 @@
#ifndef MBGL_RENDERER_FRAME_HISTORY
#define MBGL_RENDERER_FRAME_HISTORY

#include <deque>
#include <cassert>
#include <cmath>
#include <array>

#include <mbgl/platform/platform.hpp>
#include <mbgl/gl/gl_object_store.hpp>
#include <mbgl/util/chrono.hpp>

namespace mbgl {

struct FrameSnapshot {
const TimePoint now;
float z;
};

struct FadeProperties {
float fadedist;
float minfadezoom;
float maxfadezoom;
float bump;
};

class FrameHistory {
public:
// Record frame history that will be used to calculate fading params
void record(const TimePoint&, float zoom);
FrameHistory();
void record(const TimePoint&, float zoom, const Duration&);

bool needsAnimation(const Duration&) const;
FadeProperties getFadeProperties(const TimePoint&, const Duration&);
void bind(gl::GLObjectStore&);
void upload(gl::GLObjectStore&);

private:
std::deque<FrameSnapshot> history;
const int width = 256;
const int height = 1;

std::array<TimePoint, 256> changeTimes;
std::array<uint8_t, 256> changeOpacities;
std::array<uint8_t, 256> opacities;

int16_t previousZoomIndex = 0;
TimePoint previousTime = TimePoint::min();
TimePoint time = TimePoint::min();
bool firstFrame = true;
bool changed = true;

gl::TextureHolder texture;
};

} // namespace mbgl
Expand Down
6 changes: 4 additions & 2 deletions src/mbgl/renderer/painter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ void Painter::render(const Style& style, const FrameData& frame_, SpriteAtlas& a
matrix::identity(nativeMatrix);
matrix::multiply(nativeMatrix, projMatrix, nativeMatrix);

frameHistory.record(frame.timePoint, state.getZoom(),
frame.mapMode == MapMode::Continuous ? util::DEFAULT_FADE_DURATION : Milliseconds(0));

// - UPLOAD PASS -------------------------------------------------------------------------------
// Uploads all required buffers and images before we do any actual rendering.
{
Expand All @@ -117,6 +120,7 @@ void Painter::render(const Style& style, const FrameData& frame_, SpriteAtlas& a
spriteAtlas->upload(glObjectStore);
lineAtlas->upload(glObjectStore);
glyphAtlas->upload(glObjectStore);
frameHistory.upload(glObjectStore);
annotationSpriteAtlas.upload(glObjectStore);

for (const auto& item : order) {
Expand Down Expand Up @@ -161,8 +165,6 @@ void Painter::render(const Style& style, const FrameData& frame_, SpriteAtlas& a
drawClippingMasks(generator.getStencils());
}

frameHistory.record(frame.timePoint, state.getZoom());

// Actually render the layers
if (debug::renderTree) { Log::Info(Event::Render, "{"); indent++; }

Expand Down
24 changes: 7 additions & 17 deletions src/mbgl/renderer/painter_symbol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,18 +71,9 @@ void Painter::renderSDF(SymbolBucket &bucket,

sdfShader.u_zoom = (state.getZoom() - zoomAdjust) * 10; // current zoom level

if (frame.mapMode == MapMode::Continuous) {
FadeProperties f = frameHistory.getFadeProperties(frame.timePoint, util::DEFAULT_FADE_DURATION);
sdfShader.u_fadedist = f.fadedist * 10;
sdfShader.u_minfadezoom = std::floor(f.minfadezoom * 10);
sdfShader.u_maxfadezoom = std::floor(f.maxfadezoom * 10);
sdfShader.u_fadezoom = (state.getZoom() + f.bump) * 10;
} else { // MapMode::Still
sdfShader.u_fadedist = 0;
sdfShader.u_minfadezoom = state.getZoom() * 10;
sdfShader.u_maxfadezoom = state.getZoom() * 10;
sdfShader.u_fadezoom = state.getZoom() * 10;
}
config.activeTexture = GL_TEXTURE1;
frameHistory.bind(glObjectStore);
sdfShader.u_fadetexture = 1;

// The default gamma value has to be adjust for the current pixelratio so that we're not
// drawing blurry font on retina screens.
Expand Down Expand Up @@ -239,14 +230,13 @@ void Painter::renderSymbol(SymbolBucket& bucket,

// adjust min/max zooms for variable font sies
float zoomAdjust = std::log(fontSize / layout.iconSize) / std::log(2);

iconShader->u_zoom = (state.getZoom() - zoomAdjust) * 10; // current zoom level
iconShader->u_fadedist = 0 * 10;
iconShader->u_minfadezoom = state.getZoom() * 10;
iconShader->u_maxfadezoom = state.getZoom() * 10;
iconShader->u_fadezoom = state.getZoom() * 10;
iconShader->u_opacity = paint.iconOpacity;

config.activeTexture = GL_TEXTURE1;
frameHistory.bind(glObjectStore);
iconShader->u_fadetexture = 1;

setDepthSublayer(0);
bucket.drawIcons(*iconShader, glObjectStore);
}
Expand Down
7 changes: 5 additions & 2 deletions src/mbgl/shader/icon.fragment.glsl
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
uniform sampler2D u_texture;
uniform sampler2D u_fadetexture;
uniform float u_opacity;

varying vec2 v_tex;
varying float v_alpha;
varying vec2 v_fade_tex;

void main() {
gl_FragColor = texture2D(u_texture, v_tex) * v_alpha;
float alpha = texture2D(u_fadetexture, v_fade_tex).a * u_opacity;
gl_FragColor = texture2D(u_texture, v_tex) * alpha;
}
30 changes: 2 additions & 28 deletions src/mbgl/shader/icon.vertex.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,13 @@ attribute vec4 a_data2;
uniform mat4 u_matrix;
uniform mat4 u_exmatrix;
uniform float u_zoom;
uniform float u_fadedist;
uniform float u_minfadezoom;
uniform float u_maxfadezoom;
uniform float u_fadezoom;
uniform float u_opacity;
uniform bool u_skewed;
uniform float u_extra;

uniform vec2 u_texsize;

varying vec2 v_tex;
varying float v_alpha;
varying vec2 v_fade_tex;

void main() {
vec2 a_tex = a_data1.xy;
Expand All @@ -30,29 +25,9 @@ void main() {
float a_minzoom = a_zoom[0];
float a_maxzoom = a_zoom[1];

float a_fadedist = 10.0;

// u_zoom is the current zoom level adjusted for the change in font size
float z = 2.0 - step(a_minzoom, u_zoom) - (1.0 - step(a_maxzoom, u_zoom));

// fade out labels
float alpha = clamp((u_fadezoom - a_labelminzoom) / u_fadedist, 0.0, 1.0);

if (u_fadedist >= 0.0) {
v_alpha = alpha;
} else {
v_alpha = 1.0 - alpha;
}
if (u_maxfadezoom < a_labelminzoom) {
v_alpha = 0.0;
}
if (u_minfadezoom >= a_labelminzoom) {
v_alpha = 1.0;
}

// if label has been faded out, clip it
z += step(v_alpha, 0.0);

if (u_skewed) {
vec4 extrude = u_exmatrix * vec4(a_offset / 64.0, 0, 0);
gl_Position = u_matrix * vec4(a_pos + extrude.xy, 0, 1);
Expand All @@ -63,6 +38,5 @@ void main() {
}

v_tex = a_tex / u_texsize;

v_alpha *= u_opacity;
v_fade_tex = vec2(a_labelminzoom / 255.0, 0.0);
}
5 changes: 1 addition & 4 deletions src/mbgl/shader/icon_shader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,12 @@ class IconShader : public Shader {
UniformMatrix<4> u_matrix = {"u_matrix", *this};
UniformMatrix<4> u_exmatrix = {"u_exmatrix", *this};
Uniform<GLfloat> u_zoom = {"u_zoom", *this};
Uniform<GLfloat> u_fadedist = {"u_fadedist", *this};
Uniform<GLfloat> u_minfadezoom = {"u_minfadezoom", *this};
Uniform<GLfloat> u_maxfadezoom = {"u_maxfadezoom", *this};
Uniform<GLfloat> u_fadezoom = {"u_fadezoom", *this};
Uniform<GLfloat> u_opacity = {"u_opacity", *this};
Uniform<std::array<GLfloat, 2>> u_texsize = {"u_texsize", *this};
Uniform<GLint> u_skewed = {"u_skewed", *this};
Uniform<GLfloat> u_extra = {"u_extra", *this};
Uniform<GLint> u_texture = {"u_texture", *this};
Uniform<GLint> u_fadetexture = {"u_fadetexture", *this};

protected:
GLint a_offset = -1;
Expand Down
Loading

0 comments on commit 4c62a55

Please sign in to comment.