Skip to content

Commit

Permalink
Merge pull request #74 from Forceflow/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
Forceflow authored Feb 16, 2022
2 parents 7a45306 + 738e283 commit 6d22725
Show file tree
Hide file tree
Showing 13 changed files with 526 additions and 292 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/cmake.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,5 @@ jobs:
working-directory: ${{github.workspace}}/build
# Execute tests defined by the CMake configuration.
# See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
run: ctest -C ${{env.BUILD_TYPE}}
run: ctest -C ${{env.BUILD_TYPE}} --verbose

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Libmorton v0.2.8
# Libmorton v0.2.9
[![CMake](https://github.com/Forceflow/libmorton/actions/workflows/cmake.yml/badge.svg)](https://github.com/Forceflow/libmorton/actions/workflows/cmake.yml) [![license](https://img.shields.io/github/license/mashape/apistatus.svg)](https://opensource.org/licenses/MIT) [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/forceflow)

* Libmorton is a **C++ header-only library** with methods to efficiently encode/decode 64, 32 and 16-bit Morton codes and coordinates, in 2D and 3D. *Morton order* is also known as *Z-order* or *[the Z-order curve](https://en.wikipedia.org/wiki/Z-order_curve)*.
Expand Down
4 changes: 2 additions & 2 deletions include/libmorton/morton.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ namespace libmorton {
}
#else
inline uint_fast32_t morton2D_32_encode(const uint_fast16_t x, const uint_fast16_t y) {
return m2D_e_sLUT<uint_fast32_t, uint_fast16_t>(x, y);
return m2D_e_magicbits_combined(x, y);
}
inline uint_fast64_t morton2D_64_encode(const uint_fast32_t x, const uint_fast32_t y) {
return m2D_e_sLUT<uint_fast64_t, uint_fast32_t>(x, y);
Expand Down Expand Up @@ -91,7 +91,7 @@ namespace libmorton {
}
#else
inline void morton2D_32_decode(const uint_fast32_t morton, uint_fast16_t& x, uint_fast16_t& y) {
m2D_d_sLUT<uint_fast32_t, uint_fast16_t>(morton, x, y);
m2D_d_magicbits_combined(morton, x, y);
}
inline void morton2D_64_decode(const uint_fast64_t morton, uint_fast32_t& x, uint_fast32_t& y) {
m2D_d_sLUT<uint_fast64_t, uint_fast32_t>(morton, x, y);
Expand Down
25 changes: 25 additions & 0 deletions include/libmorton/morton2D.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,21 @@ namespace libmorton {
return morton2D_SplitBy2Bits<morton, coord>(x) | (morton2D_SplitBy2Bits<morton, coord>(y) << 1);
}

// ENCODE 2D 32-bit morton code - alternative version by JarkkoPFC - https://gist.github.com/JarkkoPFC/0e4e599320b0cc7ea92df45fb416d79a
// This uses the same technique as the magicbits method, but uses the upper part of a 64-bit type to split the y coordinate,
// the lower part to split the x coordinate, then merges them back together.
inline uint_fast32_t m2D_e_magicbits_combined(uint_fast16_t x, uint_fast16_t y) {
uint_fast64_t m = x | (uint_fast64_t(y) << 32); // put Y in upper 32 bits, X in lower 32 bits
m = (m | (m << 8)) & magicbit2D_masks64[2];
m = (m | (m << 4)) & magicbit2D_masks64[3];
m = (m | (m << 2)) & magicbit2D_masks64[4];
m = (m | (m << 1)) & magicbit2D_masks64[5];
m = m | (m >> 31); // merge X and Y back together
// hard cut off to 32 bits, because on some systems uint_fast32_t will be a 64-bit type, and we don't want to retain split Y-version in the upper 32 bits.
m = m & 0x00000000FFFFFFFF;
return uint_fast32_t(m);
}

// ENCODE 2D Morton code : For Loop
template<typename morton, typename coord>
inline morton m2D_e_for(const coord x, const coord y) {
Expand Down Expand Up @@ -226,6 +241,16 @@ namespace libmorton {
y = morton2D_GetSecondBits<morton, coord>(m >> 1);
}

// DECODE 2D 32-bit morton code - alternative version by JarkkoPFC - https://gist.github.com/JarkkoPFC/0e4e599320b0cc7ea92df45fb416d79a
inline void m2D_d_magicbits_combined(const uint_fast32_t morton, uint_fast16_t& x, uint_fast16_t& y) {
uint_fast64_t res = (morton | (uint_fast64_t(morton) << 31)) & magicbit2D_masks64[5];
res = (res | (res >> 1)) & magicbit2D_masks64[4];
res = (res | (res >> 2)) & magicbit2D_masks64[3];
res = (res | (res >> 4)) & magicbit2D_masks64[2];
res = res | (res >> 8);
x = uint_fast16_t(res) & 0xFFFF;
y = (uint_fast16_t(res >> 32)) & 0xFFFF;
}

// DECODE 2D morton code : For loop
template<typename morton, typename coord>
Expand Down
146 changes: 63 additions & 83 deletions test/libmorton_test.cpp

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions test/libmorton_test.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,15 @@ void printIncorrectDecoding3D(string method_tested, morton m, coord x, coord y,
<< getBitString<coord>(correct_z) << ")\n";
}

void printFailed() {
printf("\033[1;31m");
printf("Failed");
printf("\033[0m \n");
}

void printPassed() {
printf("\033[1;32m");
printf("Passed");
printf("\033[0m \n");
}

6 changes: 3 additions & 3 deletions test/makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
CXX=g++
CFLAGS=-O3 -m64 -I ..
CFLAGS=-O3 -m64 -I ../include

all: test bmi2 avx512
all: test bmi2 avx512 zen2

test:
$(CXX) $(CFLAGS) libmorton_test.cpp -o libmorton_test
Expand All @@ -13,7 +13,7 @@ avx512:
$(CXX) $(CFLAGS) -march=icelake-client libmorton_test.cpp -o libmorton_test_avx512

clean:
rm -f libmorton_test libmorton_test_bmi2 libmorton_test_avx512
rm -f libmorton_test libmorton_test_bmi2 libmorton_test_avx512 libmorton_test_zen2

zen2:
$(CXX) $(CFLAGS) -march=znver2 libmorton_test.cpp -o libmorton_test_zen2
26 changes: 14 additions & 12 deletions test/msvc2019/libmorton_test.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,13 @@
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<IncludePath>$(SolutionDir)\..\..;$(IncludePath)</IncludePath>
<IncludePath>$(SolutionDir)\..\..\include;$(IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<IncludePath>$(SolutionDir)\..\..\include;$(IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<IncludePath>$(SolutionDir)\..\..;$(IncludePath)</IncludePath>
<IncludePath>$(SolutionDir)\..\..\include;$(IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<IncludePath>$(SolutionDir)\..\..\include;$(IncludePath)</IncludePath>
Expand Down Expand Up @@ -162,18 +162,20 @@
<ClCompile Include="..\libmorton_test.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\libmorton\include\libmorton\morton.h" />
<ClInclude Include="..\..\libmorton\include\libmorton\morton2D_LUTs.h" />
<ClInclude Include="..\..\libmorton\include\libmorton\morton_AVX512BITALG.h" />
<ClInclude Include="..\..\libmorton\include\libmorton\morton_BMI.h" />
<ClInclude Include="..\..\libmorton\include\libmorton\morton2D.h" />
<ClInclude Include="..\..\libmorton\include\libmorton\morton3D.h" />
<ClInclude Include="..\..\libmorton\include\libmorton\morton3D_LUTs.h" />
<ClInclude Include="..\..\libmorton\include\libmorton\morton_common.h" />
<ClInclude Include="..\..\include\libmorton\morton.h" />
<ClInclude Include="..\..\include\libmorton\morton2D_LUTs.h" />
<ClInclude Include="..\..\include\libmorton\morton_AVX512BITALG.h" />
<ClInclude Include="..\..\include\libmorton\morton_BMI.h" />
<ClInclude Include="..\..\include\libmorton\morton2D.h" />
<ClInclude Include="..\..\include\libmorton\morton3D.h" />
<ClInclude Include="..\..\include\libmorton\morton3D_LUTs.h" />
<ClInclude Include="..\..\include\libmorton\morton_common.h" />
<ClInclude Include="..\libmorton_test.h" />
<ClInclude Include="..\morton_LUT_generators.h" />
<ClInclude Include="..\libmorton_test_2D.h" />
<ClInclude Include="..\libmorton_test_3D.h" />
<ClInclude Include="..\test2D_performance.h" />
<ClInclude Include="..\test3D_performance.h" />
<ClInclude Include="..\test2D_correctness.h" />
<ClInclude Include="..\test3D_correctness.h" />
<ClInclude Include="..\timer.h" />
<ClInclude Include="..\util.h" />
</ItemGroup>
Expand Down
22 changes: 12 additions & 10 deletions test/msvc2019/libmorton_test.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,36 @@
<ItemGroup>
<ClInclude Include="..\libmorton_test.h" />
<ClInclude Include="..\util.h" />
<ClInclude Include="..\..\libmorton\include\libmorton\morton_common.h">
<ClInclude Include="..\..\include\libmorton\morton_common.h">
<Filter>libmorton_headers</Filter>
</ClInclude>
<ClInclude Include="..\..\libmorton\include\libmorton\morton3D_LUTs.h">
<ClInclude Include="..\..\include\libmorton\morton3D_LUTs.h">
<Filter>libmorton_headers</Filter>
</ClInclude>
<ClInclude Include="..\..\libmorton\include\libmorton\morton2D_LUTs.h">
<ClInclude Include="..\..\include\libmorton\morton2D_LUTs.h">
<Filter>libmorton_headers</Filter>
</ClInclude>
<ClInclude Include="..\timer.h" />
<ClInclude Include="..\..\libmorton\include\libmorton\morton.h">
<ClInclude Include="..\..\include\libmorton\morton.h">
<Filter>libmorton_headers</Filter>
</ClInclude>
<ClInclude Include="..\..\libmorton\include\libmorton\morton3D.h">
<ClInclude Include="..\..\include\libmorton\morton3D.h">
<Filter>libmorton_headers</Filter>
</ClInclude>
<ClInclude Include="..\..\libmorton\include\libmorton\morton2D.h">
<ClInclude Include="..\..\include\libmorton\morton2D.h">
<Filter>libmorton_headers</Filter>
</ClInclude>
<ClInclude Include="..\..\libmorton\include\libmorton\morton_BMI.h">
<ClInclude Include="..\..\include\libmorton\morton_BMI.h">
<Filter>libmorton_headers</Filter>
</ClInclude>
<ClInclude Include="..\libmorton_test_3D.h" />
<ClInclude Include="..\libmorton_test_2D.h" />
<ClInclude Include="..\test3D_performance.h" />
<ClInclude Include="..\test2D_performance.h" />
<ClInclude Include="..\morton_LUT_generators.h" />
<ClInclude Include="..\..\libmorton\include\libmorton\morton_AVX512BITALG.h">
<ClInclude Include="..\..\include\libmorton\morton_AVX512BITALG.h">
<Filter>libmorton_headers</Filter>
</ClInclude>
<ClInclude Include="..\test2D_correctness.h" />
<ClInclude Include="..\test3D_correctness.h" />
</ItemGroup>
<ItemGroup>
<Text Include="..\todo.txt" />
Expand Down
87 changes: 43 additions & 44 deletions test/libmorton_test_2D.h → test/test2D_correctness.h
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
#pragma once
#include "libmorton_test.h"

using namespace std;

// Config variables (defined elsewhere)
extern size_t RAND_POOL_SIZE;
extern size_t total;
extern size_t MAX;
extern size_t CURRENT_TEST_MAX;
extern unsigned int times;
extern std::vector<uint_fast64_t> running_sums;

// Check a 2D Encode Function for correctness
// Check a 2D encode function for correctness
template <typename morton, typename coord, size_t bits>
static bool check2D_EncodeFunction(const encode_f_2D_wrapper<morton, coord> &function) {
static bool check2D_EncodeFunction(const encode_f_2D_wrapper<morton, coord>& function) {

// Number of bits which can be encoded for each field
static const size_t fieldbits = bits / 2;

Expand Down Expand Up @@ -45,9 +46,9 @@ static bool check2D_EncodeFunction(const encode_f_2D_wrapper<morton, coord> &fun
return everything_okay;
}

// Check a 2D Decode Function for correctness
// Check a 2D decode function for correctness
template <typename morton, typename coord, size_t bits>
static bool check2D_DecodeFunction(const decode_f_2D_wrapper<morton, coord> &function) {
static bool check2D_DecodeFunction(const decode_f_2D_wrapper<morton, coord>& function) {

// Number of bits usable by the encoding
static const size_t encodingbits = (bits / 2) * 2;
Expand Down Expand Up @@ -93,67 +94,65 @@ static bool check2D_DecodeFunction(const decode_f_2D_wrapper<morton, coord> &fun
return everything_okay;
}

// Check vector of 2D encode functions for correctness
template <typename morton, typename coord, size_t bits>
inline bool check2D_EncodeCorrectness(std::vector<encode_f_2D_wrapper<morton, coord>> encoders) {
printf("++ Checking correctness of 2D encoders (%zu bit) methods ... ", bits);
bool ok = true;
for (auto it = encoders.begin(); it != encoders.end(); it++) {
ok &= check2D_EncodeFunction<morton, coord, bits>(*it);
}
ok ? printf(" Passed. \n") : printf(" One or more methods failed. \n");
ok ? printPassed() : printFailed();
return ok;
}

// Check vector of 2D decode functions for correctness
template <typename morton, typename coord, size_t bits>
inline bool check2D_DecodeCorrectness(std::vector<decode_f_2D_wrapper<morton, coord>> decoders) {
printf("++ Checking correctness of 2D decoding (%zu bit) methods ... ", bits);
bool ok = true;
for (auto it = decoders.begin(); it != decoders.end(); it++) {
ok &= check2D_DecodeFunction<morton, coord, bits>(*it);
}
ok ? printf(" Passed. \n") : printf(" One or more methods failed. \n");
ok ? printPassed() : printFailed();
return ok;
}

template <typename morton, typename coord>
static double testEncode_2D_Linear_Perf(morton(*function)(coord, coord), size_t times) {
Timer timer = Timer();
morton runningsum = 0;
for (size_t t = 0; t < times; t++) {
for (coord i = 0; i < MAX; i++) {
for (coord j = 0; j < MAX; j++) {
timer.start();
runningsum += function(i, j);
timer.stop();
}
// Check a 2D Encode/Decode function for correct encode-decode process
template<typename morton, typename coord, size_t bits>
inline bool check2D_Match(const encode_f_2D_wrapper<morton, coord>& encode, decode_f_2D_wrapper<morton, coord>& decode, unsigned int times) {
bool everythingokay = true;
for (unsigned int i = 0; i < RAND_POOL_SIZE; ++i) {
coord maximum = (coord)(pow(2, floor(bits / 2.0f)) - 1.0f);
// generate random coordinates
coord x = rand() % maximum;
coord y = rand() % maximum;
coord x_result, y_result;
morton mortonresult = encode.encode(x, y);
decode.decode(mortonresult, x_result, y_result);
if ((x != x_result) | (y != y_result)) {
std::cout << "\n" << "x: " << getBitString<coord>(x) << " (" << x << ")\n";
std::cout << "y: " << getBitString<coord>(y) << " (" << y << ")\n";
std::cout << "morton: " << getBitString<morton>(mortonresult) << "(" << mortonresult << ")\n";
std::cout << "x_result: " << getBitString<coord>(x_result) << " (" << x_result << ")\n";
std::cout << "y_result: " << getBitString<coord>(y_result) << " (" << y_result << ")\n";
std::cout << bits << "-bit ";
std::cout << "using methods encode " << encode.description << " and decode " << decode.description << "\n";
everythingokay = false;
}
}
running_sums.push_back(runningsum);
return timer.elapsed_time_milliseconds / (float)times;
return everythingokay;
}

template <typename morton, typename coord>
static double testEncode_2D_Random_Perf(morton(*function)(coord, coord), size_t times) {
Timer timer = Timer();
coord maximum = ~0;
morton runningsum = 0;
coord x, y, z;

for (size_t t = 0; t < times; t++) {
// Create a pool of random numbers
std::vector<coord> randnumbers;
for (size_t i = 0; i < RAND_POOL_SIZE; i++) {
randnumbers.push_back(rand() % maximum);
}
// Do the performance test
for (size_t i = 0; i < total; i++) {
x = randnumbers[i % RAND_POOL_SIZE];
y = randnumbers[(i + 1) % RAND_POOL_SIZE];
timer.start();
runningsum += function(x, y);
timer.stop();
template <typename morton, typename coord, size_t bits>
inline bool check2D_EncodeDecodeMatch(std::vector<encode_f_2D_wrapper<morton, coord>> encoders, std::vector<decode_f_2D_wrapper<morton, coord>> decoders, unsigned int times) {
printf("++ Checking 2D methods (%zd bit) encode/decode match ...", bits);
bool ok = true;
for (auto et = encoders.begin(); et != encoders.end(); et++) {
for (auto dt = decoders.begin(); dt != decoders.end(); dt++) {
ok &= check2D_Match<morton, coord, bits>(*et, *dt, times);
}
}
running_sums.push_back(runningsum);
return timer.elapsed_time_milliseconds / (float)times;
}
ok ? printPassed() : printFailed();
return ok;
}
Loading

0 comments on commit 6d22725

Please sign in to comment.