mirror of
https://github.com/felt/tippecanoe.git
synced 2026-10-06 10:25:41 +02:00
Use the faster bit interleave for encode_quadkey itself
encode_vertex() computed exactly the same thing as encode_quadkey(), so there is no reason to have both. Give encode_quadkey() the branch-free implementation, which also speeds up the default encode_index, and have the shared node code call it directly. The unit test now compares it against the old bit-at-a-time loop and checks that decode_quadkey() reverses it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P2nBqZisxNQfmEmon3vE9v
This commit is contained in:
+1
-1
@@ -249,7 +249,7 @@ void add_shared_node_to_bloom(std::string &shared_nodes_bloom, unsigned long lon
|
|||||||
// Is the vertex at world coordinates wx, wy one of the nodes in the global list of shared nodes?
|
// Is the vertex at world coordinates wx, wy one of the nodes in the global list of shared nodes?
|
||||||
bool is_shared_node(long long wx, long long wy, struct node const *shared_nodes_map, size_t nodepos, std::string const &shared_nodes_bloom) {
|
bool is_shared_node(long long wx, long long wy, struct node const *shared_nodes_map, size_t nodepos, std::string const &shared_nodes_bloom) {
|
||||||
struct node n;
|
struct node n;
|
||||||
n.index = encode_vertex((unsigned) wx, (unsigned) wy);
|
n.index = encode_quadkey((unsigned) wx, (unsigned) wy);
|
||||||
|
|
||||||
size_t word;
|
size_t word;
|
||||||
unsigned long long mask, bits;
|
unsigned long long mask, bits;
|
||||||
|
|||||||
@@ -2122,7 +2122,7 @@ std::pair<int, metadata> read_input(std::vector<source> &sources, char *fname, i
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct node n;
|
struct node n;
|
||||||
n.index = encode_vertex((unsigned) x, (unsigned) y);
|
n.index = encode_quadkey((unsigned) x, (unsigned) y);
|
||||||
|
|
||||||
fwrite_check((char *) &n, sizeof(struct node), 1, readers[0].nodefile, &readers[0].nodepos, "vertices");
|
fwrite_check((char *) &n, sizeof(struct node), 1, readers[0].nodefile, &readers[0].nodepos, "vertices");
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-29
@@ -156,19 +156,20 @@ void decode_hilbert(unsigned long long index, unsigned *wx, unsigned *wy) {
|
|||||||
hilbert_d2xy(1LL << 32, index, wx, wy);
|
hilbert_d2xy(1LL << 32, index, wx, wy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Spread the 32 bits of v out into the even bits of a 64-bit value
|
||||||
|
static inline unsigned long long spread_bits(unsigned int v) {
|
||||||
|
unsigned long long x = v;
|
||||||
|
x = (x | (x << 16)) & 0x0000FFFF0000FFFFULL;
|
||||||
|
x = (x | (x << 8)) & 0x00FF00FF00FF00FFULL;
|
||||||
|
x = (x | (x << 4)) & 0x0F0F0F0F0F0F0F0FULL;
|
||||||
|
x = (x | (x << 2)) & 0x3333333333333333ULL;
|
||||||
|
x = (x | (x << 1)) & 0x5555555555555555ULL;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interleave the bits of wx and wy, with each bit of wx above the same bit of wy
|
||||||
unsigned long long encode_quadkey(unsigned int wx, unsigned int wy) {
|
unsigned long long encode_quadkey(unsigned int wx, unsigned int wy) {
|
||||||
unsigned long long out = 0;
|
return (spread_bits(wx) << 1) | spread_bits(wy);
|
||||||
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < 32; i++) {
|
|
||||||
unsigned long long v = ((wx >> (32 - (i + 1))) & 1) << 1;
|
|
||||||
v |= (wy >> (32 - (i + 1))) & 1;
|
|
||||||
v = v << (64 - 2 * (i + 1));
|
|
||||||
|
|
||||||
out |= v;
|
|
||||||
}
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::atomic<unsigned char> decodex[256];
|
static std::atomic<unsigned char> decodex[256];
|
||||||
@@ -217,20 +218,3 @@ void set_projection_or_exit(const char *optarg) {
|
|||||||
exit(EXIT_ARGS);
|
exit(EXIT_ARGS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spread the 32 bits of v out into the even bits of a 64-bit value
|
|
||||||
static inline unsigned long long spread_bits(unsigned int v) {
|
|
||||||
unsigned long long x = v;
|
|
||||||
x = (x | (x << 16)) & 0x0000FFFF0000FFFFULL;
|
|
||||||
x = (x | (x << 8)) & 0x00FF00FF00FF00FFULL;
|
|
||||||
x = (x | (x << 4)) & 0x0F0F0F0F0F0F0F0FULL;
|
|
||||||
x = (x | (x << 2)) & 0x3333333333333333ULL;
|
|
||||||
x = (x | (x << 1)) & 0x5555555555555555ULL;
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The same as encode_quadkey(), but faster, for the vertices of the list of shared nodes,
|
|
||||||
// so that nodes that are near each other are also near each other in the sorted list.
|
|
||||||
unsigned long long encode_vertex(unsigned int wx, unsigned int wy) {
|
|
||||||
return (spread_bits(wx) << 1) | spread_bits(wy);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -26,6 +26,5 @@ void decode_quadkey(unsigned long long index, unsigned *wx, unsigned *wy);
|
|||||||
unsigned long long encode_hilbert(unsigned int wx, unsigned int wy);
|
unsigned long long encode_hilbert(unsigned int wx, unsigned int wy);
|
||||||
void decode_hilbert(unsigned long long index, unsigned *wx, unsigned *wy);
|
void decode_hilbert(unsigned long long index, unsigned *wx, unsigned *wy);
|
||||||
|
|
||||||
unsigned long long encode_vertex(unsigned int wx, unsigned int wy);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+1
-1
@@ -475,7 +475,7 @@ static void add_scaled_node(struct reader *r, serialization_state *sst, draw g)
|
|||||||
long long y = SHIFT_LEFT(g.y);
|
long long y = SHIFT_LEFT(g.y);
|
||||||
|
|
||||||
struct node n;
|
struct node n;
|
||||||
n.index = encode_vertex((unsigned) x, (unsigned) y);
|
n.index = encode_quadkey((unsigned) x, (unsigned) y);
|
||||||
|
|
||||||
fwrite_check((char *) &n, sizeof(struct node), 1, r->nodefile, &r->nodepos, sst->fname);
|
fwrite_check((char *) &n, sizeof(struct node), 1, r->nodefile, &r->nodepos, sst->fname);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -129,11 +129,31 @@ TEST_CASE("Bit reversal", "bit reversal") {
|
|||||||
REQUIRE(bit_reverse(0xF3D912481E6A2C48) == 0x1234567812489BCF);
|
REQUIRE(bit_reverse(0xF3D912481E6A2C48) == 0x1234567812489BCF);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Vertex encoding matches quadkey encoding", "[projection]") {
|
// The bit-at-a-time quadkey encoding that encode_quadkey() used to use
|
||||||
|
static unsigned long long reference_quadkey(unsigned int wx, unsigned int wy) {
|
||||||
|
unsigned long long out = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < 32; i++) {
|
||||||
|
unsigned long long v = ((wx >> (32 - (i + 1))) & 1) << 1;
|
||||||
|
v |= (wy >> (32 - (i + 1))) & 1;
|
||||||
|
v = v << (64 - 2 * (i + 1));
|
||||||
|
|
||||||
|
out |= v;
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Quadkey encoding", "[projection]") {
|
||||||
unsigned int values[] = {0, 1, 2, 0x7FFFFFFF, 0x80000000, 0xFFFFFFFF, 0x12345678, 0xDEADBEEF};
|
unsigned int values[] = {0, 1, 2, 0x7FFFFFFF, 0x80000000, 0xFFFFFFFF, 0x12345678, 0xDEADBEEF};
|
||||||
for (unsigned int x : values) {
|
for (unsigned int x : values) {
|
||||||
for (unsigned int y : values) {
|
for (unsigned int y : values) {
|
||||||
REQUIRE(encode_vertex(x, y) == encode_quadkey(x, y));
|
REQUIRE(encode_quadkey(x, y) == reference_quadkey(x, y));
|
||||||
|
|
||||||
|
unsigned wx, wy;
|
||||||
|
decode_quadkey(encode_quadkey(x, y), &wx, &wy);
|
||||||
|
REQUIRE(wx == x);
|
||||||
|
REQUIRE(wy == y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,7 +162,7 @@ TEST_CASE("Vertex encoding matches quadkey encoding", "[projection]") {
|
|||||||
seed = seed * 6364136223846793005ULL + 1442695040888963407ULL;
|
seed = seed * 6364136223846793005ULL + 1442695040888963407ULL;
|
||||||
unsigned int x = seed >> 32;
|
unsigned int x = seed >> 32;
|
||||||
unsigned int y = seed;
|
unsigned int y = seed;
|
||||||
REQUIRE(encode_vertex(x, y) == encode_quadkey(x, y));
|
REQUIRE(encode_quadkey(x, y) == reference_quadkey(x, y));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user