retain child tiles

This commit is contained in:
Konstantin Käfer
2014-01-15 11:34:41 +01:00
parent 60a8fdca57
commit 44a0f98fe5
11 changed files with 251 additions and 169 deletions

View File

@@ -4,11 +4,12 @@
#include "../platform/platform.hpp"
#include "../renderer/painter.hpp"
#include "transform.hpp"
#include <llmr/map/tile.hpp>
#include <forward_list>
namespace llmr {
class tile;
class map {
public:
map();
@@ -22,20 +23,22 @@ public:
void moveBy(double dx, double dy);
void scaleBy(double ds, double cx, double cy);
void rotateBy(double cx, double cy, double sx, double sy, double ex, double ey);
void tileLoaded(std::shared_ptr<tile> tile);
void tileFailed(std::shared_ptr<tile> tile);
void tileLoaded(tile::ptr tile);
void tileFailed(tile::ptr tile);
private:
void updateTiles();
tile::ptr addTile(tile_id id);
tile::ptr hasTile(tile_id id);
private:
transform *transform;
painter *painter;
// int32_t min_zoom;
// int32_t max_zoom;
int32_t min_zoom;
int32_t max_zoom;
std::vector<std::shared_ptr<tile>> tiles;
std::forward_list<tile::ptr> tiles;
};
}

View File

@@ -4,18 +4,26 @@
#include "../geometry/linevertexbuffer.hpp"
#include <stdint.h>
#include <vector>
#include <forward_list>
#include <mutex>
#include <llmr/util/vec2.hpp>
namespace llmr {
typedef vec3<int32_t> tile_id;
class tile {
public:
typedef std::shared_ptr<tile> ptr;
enum state {
initial,
loading,
ready,
obsolete
};
public:
tile(int32_t z, int32_t x, int32_t y);
tile(tile_id id);
~tile();
void setData(uint8_t *data, uint32_t bytes);
@@ -26,18 +34,20 @@ public:
void cancel();
const std::string toString() const;
static tile_id parent(const tile_id& id, int32_t z);
static std::forward_list<tile_id> children(const tile_id& id, int32_t z);
// static int64_t toID(int32_t z, int32_t x, int32_t y, int32_t w = 0);
// static vec4<int32_t> fromID(int64_t id);
public:
const int32_t z, x, y;
bool loaded;
bool cancelled;
const tile_id id;
state state;
linevertexbuffer lineVertex;
private:
std::mutex mutex;
// Source data
uint8_t *data;
uint32_t bytes;

View File

@@ -3,6 +3,7 @@
#include <cstdint>
#include <llmr/util/vec2.hpp>
namespace llmr {
@@ -26,7 +27,7 @@ public:
void setLonLat(double lon, double lat);
// Getters
void matrixFor(float matrix[16], uint32_t z, uint32_t x, uint32_t y) const;
void matrixFor(float matrix[16], const vec3<int32_t>& id) const;
int32_t getZoom() const;
void getLonLat(double& lon, double& lat) const;

View File

@@ -21,7 +21,8 @@ public:
void clear();
void render(std::shared_ptr<tile> tile);
void switchShader(Shader *shader, float matrix[16]);
void drawClippingMask();
void switchShader(Shader *shader);
private:
void setupShaders();
@@ -37,9 +38,7 @@ private:
FillShader *fillShader;
LineShader *lineShader;
// uint32_t vertexArray;
uint32_t triangleVertexBuffer;
uint32_t fillVertexBuffer;
uint32_t tile_stencil_buffer;
};
}

View File

@@ -7,6 +7,9 @@ template <typename T = double>
struct vec2 {
T x, y;
inline vec2() {}
inline vec2(const vec2& o) : x(o.x), y(o.y) {}
inline vec2(T x, T y) : x(x), y(y) {}
inline bool operator==(const vec2& rhs) const {
return x == rhs.x && y == rhs.y;
}
@@ -16,6 +19,9 @@ template <typename T = double>
struct vec3 {
T x, y, z;
inline vec3() {}
inline vec3(const vec3& o) : x(o.x), y(o.y), z(o.z) {}
inline vec3(T x, T y, T z) : x(x), y(y), z(z) {}
inline bool operator==(const vec3& rhs) const {
return x == rhs.x && y == rhs.y && z == rhs.z;
}
@@ -25,6 +31,9 @@ template <typename T = double>
struct vec4 {
T x, y, z, w;
inline vec4() {}
inline vec4(const vec4& o) : x(o.x), y(o.y), z(o.z), w(o.w) {}
inline vec4(T x, T y, T z, T w) : x(x), y(y), z(z), w(w) {}
inline bool operator==(const vec4& rhs) const {
return x == rhs.x && y == rhs.y && z == rhs.z && w == rhs.w;
}

View File

@@ -20,6 +20,9 @@ public:
exit(1);
}
glfwWindowHint(GLFW_STENCIL_BITS, 8);
glfwWindowHint(GLFW_DEPTH_BITS, 16);
window = glfwCreateWindow(640, 480, "llmr", NULL, NULL);
if (!window) {
glfwTerminate();
@@ -29,6 +32,9 @@ public:
glfwMakeContextCurrent(window);
int stencil_bits = glfwGetWindowAttrib(window, GLFW_STENCIL_BITS);
fprintf(stderr, "stencil bits: %d\n", stencil_bits);
map->setup();
@@ -173,6 +179,7 @@ void restart(void *) {
}
void request(void *, tile::ptr tile) {
assert((bool)tile);
// fprintf(stderr, "request %d/%d/%d\n", tile->z, tile->x, tile->y);
// fprintf(stderr, "requesting tile\n");
@@ -180,9 +187,9 @@ void request(void *, tile::ptr tile) {
NSString *urlTemplate = @"http://localhost:3333/gl/tiles/plain/%d-%d-%d.vector.pbf";
NSString *urlString = [NSString
stringWithFormat:urlTemplate,
tile->z,
tile->x,
tile->y];
tile->id.z,
tile->id.x,
tile->id.y];
NSURL *url = [NSURL URLWithString:urlString];
// NSLog(@"Requesting %@", urlString);
@@ -210,12 +217,9 @@ void request(void *, tile::ptr tile) {
});
return;
}
} else {
fprintf(stderr, "[%s] status code %d\n", [urlString UTF8String], code);
dispatch_async(dispatch_get_main_queue(), ^ {
view->map->tileFailed(tile);
});
}
fprintf(stderr, "[%s] status code %d\n", [urlString UTF8String], code);
}
dispatch_async(dispatch_get_main_queue(), ^ {

View File

@@ -6,12 +6,15 @@
#include <thread>
#include <cmath>
#include <cassert>
using namespace llmr;
map::map()
: transform(new class transform()),
painter(new class painter(transform)) {
painter(new class painter(transform)),
min_zoom(0),
max_zoom(14) {
// transform->setLonLat(13, 50);
// transform->setZoom(3);
@@ -50,9 +53,38 @@ void map::rotateBy(double cx, double cy, double sx, double sy, double ex, double
platform::restart(this);
}
tile::ptr map::hasTile(tile_id id) {
for (tile::ptr& tile : tiles) {
if (tile->id == id) {
return tile;
}
}
return tile::ptr();
}
tile::ptr map::addTile(tile_id id) {
tile::ptr tile = hasTile(id);
if (!tile.get()) {
// We couldn't find the tile in the list. Create a new one.
tile = std::make_shared<class tile>(id);
assert(tile);
std::cerr << "init " << id.z << "/" << id.x << "/" << id.y << std::endl;
std::cerr << "add " << tile->toString() << std::endl;
tiles.push_front(tile);
}
return tile;
}
void map::updateTiles() {
// Figure out what tiles we need to load
int32_t zoom = transform->getZoom();
if (zoom > max_zoom) zoom = max_zoom;
if (zoom < min_zoom) zoom = min_zoom;
int32_t max_dim = pow(2, zoom);
// Map four viewport corners to pixel coordinates
@@ -65,55 +97,75 @@ void map::updateTiles() {
br.x = fmin(max_dim, ceil(fmax(box.tr.x, box.br.x)));
br.y = fmin(max_dim, ceil(fmax(box.bl.y, box.br.y)));
typedef vec3<int32_t> tile_id;
// TODO: Discard tiles that are outside the viewport
std::vector<tile_id> required;
std::forward_list<tile_id> required;
for (int32_t y = tl.y; y < br.y; y++) {
for (int32_t x = tl.x; x < br.x; x++) {
tile_id id; id.z = zoom; id.x = x; id.y = y;
required.push_back(id);
required.emplace_front(x, y, zoom);
}
}
std::forward_list<tile_id> retain(required);
// Add existing child/parent tiles if the actual tile is not yet loaded
// TODO
for (const tile_id& id : required) {
tile::ptr tile = addTile(id);
assert(tile);
if (tile->state == tile::initial || tile->state == tile::loading) {
// The tile we require is not yet loaded. Try to find a parent or
// child tile that we already have.
std::forward_list<tile_id> covering;
// Find existing child tiles.
// TODO: Limit to existing tile ranges.
if (id.z + 1 <= max_zoom) {
covering = tile::children(id, id.z + 1);
}
// Find existing parent tile.
// TODO: Limit to existing tile ranges.
if (id.z - 1 >= min_zoom) {
covering.push_front(tile::parent(id, id.z - 1));
}
for (const tile_id& covering_id : covering) {
tile::ptr covering_tile = hasTile(covering_id);
if (covering_tile) {
assert(covering_tile);
if (covering_tile->state == tile::ready) {
retain.emplace_front(covering_tile->id);
}
}
}
}
if (tile->state == tile::initial) {
// If the tile is new, we have to make sure to load it.
tile->state = tile::loading;
platform::request(this, tile);
}
}
// Remove tiles that we definitely don't need, i.e. tiles that are not on
// the required list.
for (std::vector<tile::ptr>::iterator it = tiles.begin(); it != tiles.end(); it++) {
tile::ptr& tile = *it;
tile_id id; id.z = tile->z; id.x = tile->x; id.y = tile->y;
std::vector<tile_id>::const_iterator required_it = std::find(required.begin(), required.end(), id);
bool retain = required_it != required.end();
if (retain) {
// We already have the tile; remove it from the list of required tiles.
required.erase(required_it);
} else {
// We don't need this tile, so we can remove it entirely.
std::vector<tile::ptr>::iterator to_remove = it - 1;
tiles.remove_if([&retain](const tile::ptr& tile) {
assert(tile);
bool obsolete = std::find(retain.begin(), retain.end(), tile->id) == retain.end();
if (obsolete) {
tile->cancel();
tiles.erase(it);
it = to_remove;
}
}
// Required now only contains those tiles that are not yet in the list of
// tiles, so we should add them all.
for (tile_id& id : required) {
tile::ptr tile = std::make_shared<class tile>(id.z, id.x, id.y);
tiles.push_back(tile);
platform::request(this, tile);
}
return obsolete;
});
}
bool map::render() {
painter->clear();
for (tile::ptr& tile : tiles) {
// painter->viewport();
if (tile->loaded) {
assert(tile);
if (tile->state == tile::ready) {
painter->render(tile);
}
}
@@ -122,6 +174,7 @@ bool map::render() {
}
void map::tileLoaded(tile::ptr tile) {
std::cerr << "loaded " << tile->toString() << std::endl;
platform::restart(this);
}

View File

@@ -7,6 +7,7 @@
#include "pbf.hpp"
#include <llmr/util/vec2.hpp>
#include <llmr/util/string.hpp>
#include <cmath>
using namespace llmr;
@@ -34,49 +35,70 @@ using namespace llmr;
// return coord;
// };
tile::tile(int32_t z, int32_t x, int32_t y)
: z(z),
x(x),
y(y),
loaded(false),
cancelled(false),
tile_id tile::parent(const tile_id& id, int32_t z) {
assert(z < id.z);
tile_id pos(id);
while (pos.z > z) {
pos.z--;
pos.x = floor(pos.x / 2);
pos.y = floor(pos.y / 2);
}
return pos;
}
std::forward_list<tile_id> tile::children(const tile_id& id, int32_t z) {
assert(z > id.z);
int32_t factor = pow(2, z - id.z);
std::forward_list<tile_id> children;
for (int32_t y = id.y * factor, y_max = (id.y + 1) * factor; y < y_max; y++) {
for (int32_t x = id.x * factor, x_max = (id.x + 1) * factor; x < x_max; x++) {
children.emplace_front(x, y, z);
}
}
return children;
}
tile::tile(tile_id id)
: id(id),
state(initial),
data(0),
bytes(0) {
}
tile::~tile() {
fprintf(stderr, "[%p] deleting tile %d/%d/%d\n", this, z, x, y);
fprintf(stderr, "[%p] deleting tile %d/%d/%d\n", this, id.z, id.x, id.y);
if (this->data) {
free(this->data);
}
}
const std::string tile::toString() const {
return util::sprintf("[tile %d/%d/%d]", id.z, id.x, id.y);
}
void tile::setData(uint8_t *data, uint32_t bytes) {
this->data = (uint8_t *)malloc(bytes);
this->bytes = bytes;
memcpy(this->data, data, bytes);
}
void tile::cancel()
{
std::lock_guard<std::mutex> lock(mutex);
void tile::cancel() {
// TODO: thread safety
if (!cancelled) {
cancelled = true;
if (state != obsolete) {
state = obsolete;
} else {
assert((!"logic error? multiple cancelleations"));
}
}
bool tile::parse()
{
{
std::lock_guard<std::mutex> lock(mutex);
if (cancelled) {
bool tile::parse() {
if (state == obsolete) {
return false;
}
}
// fprintf(stderr, "[%p] parsing tile [%d/%d/%d]...\n", this, z, x, y);
@@ -84,7 +106,7 @@ bool tile::parse()
int code = setjmp(tile.jump_buffer);
if (code > 0) {
fprintf(stderr, "[%p] parsing tile [%d/%d/%d]... failed: %s\n", this, z, x, y, tile.msg.c_str());
fprintf(stderr, "[%p] parsing tile [%d/%d/%d]... failed: %s\n", this, id.z, id.x, id.y, tile.msg.c_str());
cancel();
return false;
}
@@ -101,14 +123,12 @@ bool tile::parse()
// fprintf(stderr, "[%p] parsing tile [%d/%d/%d]... done\n", this, z, x, y);
{
std::lock_guard<std::mutex> lock(mutex);
if (cancelled) {
if (state == obsolete) {
return false;
}
} else {
state = ready;
}
loaded = true;
return true;
}

View File

@@ -1,7 +1,6 @@
#include <llmr/map/transform.hpp>
#include <llmr/util/mat4.h>
#include <llmr/util/vec2.hpp>
#include <llmr/util/math.hpp>
#include <cmath>
#include <cstdio>
@@ -131,8 +130,8 @@ double transform::pixel_y() const {
return center + y;
}
void transform::matrixFor(float matrix[16], uint32_t tile_z, uint32_t tile_x, uint32_t tile_y) const {
const double tile_scale = pow(2, tile_z);
void transform::matrixFor(float matrix[16], const vec3<int32_t>& id) const {
const double tile_scale = pow(2, id.z);
const double tile_size = scale * size / tile_scale;
mat4_identity(matrix);
@@ -142,7 +141,7 @@ void transform::matrixFor(float matrix[16], uint32_t tile_z, uint32_t tile_x, ui
mat4_translate(matrix, matrix, -0.5f * (float)width, -0.5f * (float)height, 0);
mat4_translate(matrix, matrix, pixel_x(), pixel_y(), 0);
mat4_translate(matrix, matrix, tile_x * tile_size, tile_y * tile_size, 0);
mat4_translate(matrix, matrix, id.x * tile_size, id.y * tile_size, 0);
// TODO: Get rid of the 8 (scaling from 4096 to 512 px tile size);
mat4_scale(matrix, matrix, scale / tile_scale / 8, scale / tile_scale / 8, 1);

View File

@@ -11,49 +11,37 @@ using namespace llmr;
#define BUFFER_OFFSET(i) ((char *)NULL + (i))
GLfloat triangle_vertices[] = {
100, 100,
100, 300,
150, 150
};
GLfloat fill_vertices[] = {
GLshort tile_stencil_vertices[] = {
0, 0,
300, 0,
0, 300,
300, 300
4096, 0,
0, 4096,
4096, 4096
};
painter::painter(class transform *transform)
: transform(transform),
currentShader(NULL),
fillShader(NULL) {
fillShader(NULL),
lineShader(NULL) {
}
void painter::setup() {
setupShaders();
assert(fillShader);
assert(lineShader);
// Set up the stencil quad we're using to generate the stencil mask.
glGenBuffers(1, &tile_stencil_buffer);
glBindBuffer(GL_ARRAY_BUFFER, tile_stencil_buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(tile_stencil_vertices), tile_stencil_vertices, GL_STATIC_DRAW);
glEnable(GL_STENCIL_TEST);
glGenBuffers(1, &triangleVertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, triangleVertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(triangle_vertices), triangle_vertices, GL_STATIC_DRAW);
glGenBuffers(1, &fillVertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, fillVertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(fill_vertices), fill_vertices, GL_STATIC_DRAW);
// glGenVertexArraysOES(1, &vertexArray);
// glBindVertexArrayOES(vertexArray);
// glBindVertexArrayOES(0);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
void painter::setupShaders() {
@@ -62,10 +50,7 @@ void painter::setupShaders() {
}
void painter::teardown() {
glDeleteBuffers(1, &triangleVertexBuffer);
glDeleteBuffers(1, &fillVertexBuffer);
// glDeleteVertexArraysOES(1, &vertexArray);
glDeleteBuffers(1, &tile_stencil_buffer);
if (fillShader) {
delete fillShader;
@@ -87,74 +72,74 @@ void painter::changeMatrix(std::shared_ptr<tile> tile) {
mat4_ortho(projMatrix, 0, transform->width, transform->height, 0, 1, 10);
float mvMatrix[16];
transform->matrixFor(mvMatrix, tile->z, tile->x, tile->y);
transform->matrixFor(mvMatrix, tile->id);
mat4_multiply(matrix, projMatrix, mvMatrix);
}
void painter::drawClippingMask() {
switchShader(lineShader);
glUniformMatrix4fv(lineShader->u_matrix, 1, GL_FALSE, matrix);
glColorMask(false, false, false, false);
// Clear the entire stencil buffer, except for the 7th bit, which stores
// the global clipping mask that allows us to avoid drawing in regions of
// tiles we've already painted in.
glClearStencil(0x0);
glStencilMask(0xBF);
glClear(GL_STENCIL_BUFFER_BIT);
// The stencil test will fail always, meaning we set all pixels covered
// by this geometry to 0x80. We use the highest bit 0x80 to mark the regions
// we want to draw in. All pixels that have this bit *not* set will never be
// drawn in.
glStencilFunc(GL_EQUAL, 0xC0, 0x40);
glStencilMask(0xC0);
glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP);
// Draw the clipping mask
glBindBuffer(GL_ARRAY_BUFFER, tile_stencil_buffer);
glVertexAttribPointer(lineShader->a_pos, 2, GL_SHORT, false, 0, BUFFER_OFFSET(0));
glUniform4f(lineShader->u_color, 1.0f, 0.0f, 1.0f, 1.0f);
glDrawArrays(GL_TRIANGLE_STRIP, 0, sizeof(tile_stencil_vertices));
// glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_EQUAL, 0x80, 0x80);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glStencilMask(0x00);
glColorMask(true, true, true, true);
}
void painter::clear() {
glClearColor(1.0f, 1.0f, 0.0f, 1.0f);
glClearStencil(0x0);
glStencilMask(0xFF);
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
}
void painter::render(std::shared_ptr<tile> tile) {
void painter::render(tile::ptr tile) {
if (tile->state != tile::ready) {
return;
}
changeMatrix(tile);
glDisable(GL_STENCIL_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
drawClippingMask();
// fprintf(stderr, "render tile\n");
switchShader(lineShader, matrix);
switchShader(lineShader);
glUniformMatrix4fv(lineShader->u_matrix, 1, GL_FALSE, matrix);
// glEnable(GL_BLEND);
// glVertexAttribPointer(fillShader->a_pos, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
// glVertexAttribPointer(fillShader->a_pos, 2, GL_SHORT, GL_FALSE, 0, BUFFER_OFFSET(0));
// glBindBuffer(GL_ARRAY_BUFFER, triangleVertexBuffer);
glStencilFunc(GL_EQUAL, 0x80, 0x80);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
// glDisable(GL_STENCIL_TEST);
tile->lineVertex.bind();
glVertexAttribPointer(lineShader->a_pos, 2, GL_SHORT, GL_FALSE, 0, BUFFER_OFFSET(0));
glUniform4f(lineShader->u_color, 0.0f, 0.0f, 0.0f, 1.0f);
glLineWidth(1.0f);
glDrawArrays(GL_LINE_STRIP, 0, tile->lineVertex.length());
// switchShader(fillShader, matrix);
// glBindBuffer(GL_ARRAY_BUFFER, triangleVertexBuffer);
// glVertexAttribPointer(fillShader->a_pos, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
// glUniform4f(fillShader->u_color, 0.0f, 1.0f, 0.0f, 1.0f);
// glDrawArrays(GL_TRIANGLE_STRIP, 0, 3);
// glBindVertexArrayOES(vertexArray);
// switchShader(fillShader, matrix);
// glUniformMatrix4fv(fillShader->u_matrix, 1, GL_FALSE, matrix);
// glEnable(GL_STENCIL_TEST);
// Draw stencil mask.
// glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
// glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP);
// glStencilFunc(GL_EQUAL, 0xFF, 0xFF);
// glStencilMask(0xFF);
// glBindBuffer(GL_ARRAY_BUFFER, triangleVertexBuffer);
// glVertexAttribPointer(fillShader->a_pos, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
// glUniform4f(fillShader->u_color, 0.0f, 1.0f, 0.0f, 1.0f);
// glDrawArrays(GL_TRIANGLE_STRIP, 0, 3);
// // Draw covering fill.
// glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
// glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
// glStencilFunc(GL_EQUAL, 0xFF, 0xFF);
// glBindBuffer(GL_ARRAY_BUFFER, fillVertexBuffer);
// glVertexAttribPointer(fillShader->a_pos, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
// glUniform4f(fillShader->u_color, 1.0f, 0.0f, 0.0f, 1.0f);
// glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
void painter::viewport() {
@@ -162,7 +147,7 @@ void painter::viewport() {
}
// Switches to a different shader program.
void painter::switchShader(Shader *shader, float matrix[16]) {
void painter::switchShader(Shader *shader) {
if (currentShader != shader) {
glUseProgram(shader->program);

View File

@@ -1,4 +1,3 @@
uniform vec4 u_color;
void main() {