簡體   English   中英

2D莫頓碼編碼/解碼64位

[英]2D morton code encode/decode 64bits

如何將給定[x,y]的莫頓碼(z階)編碼/解碼為產生64位morton碼的32位無符號整數,反之亦然? 我確實有xy2d和d2xy,但僅適用於16位寬的坐標,產生32位morton數。 在網上搜索了很多,但找不到。 請幫忙。

如果您可以使用特定於體系結構的指令,那么您將有可能將操作加速到超出使用比特流黑客的能力之外:

例如,如果您為Intel Haswell和更高版本的CPU編寫代碼,則可以使用包含pextpdep指令的BMI2指令集。 這些(除其他功能外)可用於構建您的功能。

這是一個完整的示例(已通過GCC測試):

#include <immintrin.h>
#include <stdint.h>

// on GCC, compile with option -mbmi2, requires Haswell or better.

uint64_t xy_to_morton(uint32_t x, uint32_t y)
{
  return _pdep_u32(x, 0x55555555) | _pdep_u32(y,0xaaaaaaaa);
}

void morton_to_xy(uint64_t m, uint32_t *x, uint32_t *y)
{
  *x = _pext_u64(m, 0x5555555555555555);
  *y = _pext_u64(m, 0xaaaaaaaaaaaaaaaa);
}

如果您必須支持較早的CPU或ARM平台,那么一切都不會丟失。 您可能仍然至少從特定於密碼學的說明中獲得xy_to_morton函數的幫助。

如今,許多CPU都支持無進位乘法。 在ARM上, vmul_p8是NEON指令集中的vmul_p8 在X86上,您可以從CLMUL指令集中找到它作為PCLMULQDQ (自2010年以來可用)。

這里的技巧是,數字與自身的無進位乘法將返回一個位模式,該模式包含參數的原始位以及零位交錯。 因此,它與上面顯示的_pdep_u32(x,0x55​​555555)相同。 例如,它將變成以下字節:

 +----+----+----+----+----+----+----+----+
 | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 |
 +----+----+----+----+----+----+----+----+

進入:

 +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
 | 0  | b7 | 0  | b6 | 0  | b5 | 0  | b4 | 0  | b3 | 0  | b2 | 0  | b1 | 0  | b0 |
 +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+

現在,您可以按如下方式構建xy_to_morton函數(此處顯示為CLMUL指令集):

#include <wmmintrin.h>
#include <stdint.h>

// on GCC, compile with option -mpclmul

uint64_t carryless_square (uint32_t x)
{
  uint64_t val[2] = {x, 0};
  __m128i *a = (__m128i * )val;
  *a = _mm_clmulepi64_si128 (*a,*a,0);
  return val[0];
}

uint64_t xy_to_morton (uint32_t x, uint32_t y)
{
  return carryless_square(x)|(carryless_square(y) <<1);
}

_mm_clmulepi64_si128生成128位結果,我們僅使用低64位。 因此,您甚至可以改進上述版本,並使用一個_mm_clmulepi64_si128來完成此工作。

這與主流平台(例如帶有NEON和x86的現代ARM)上的性能一樣好。 不幸的是,我不知道使用密碼學指令來加速morton_to_xy函數的任何技巧,而且我花了幾個月的時間努力嘗試。

void xy2d_morton(uint64_t x, uint64_t y, uint64_t *d)
{
    x = (x | (x << 16)) & 0x0000FFFF0000FFFF;
    x = (x | (x << 8)) & 0x00FF00FF00FF00FF;
    x = (x | (x << 4)) & 0x0F0F0F0F0F0F0F0F;
    x = (x | (x << 2)) & 0x3333333333333333;
    x = (x | (x << 1)) & 0x5555555555555555;

    y = (y | (y << 16)) & 0x0000FFFF0000FFFF;
    y = (y | (y << 8)) & 0x00FF00FF00FF00FF;
    y = (y | (y << 4)) & 0x0F0F0F0F0F0F0F0F;
    y = (y | (y << 2)) & 0x3333333333333333;
    y = (y | (y << 1)) & 0x5555555555555555;

    *d = x | (y << 1);
}

// morton_1 - extract even bits

uint32_t morton_1(uint64_t x)
{
    x = x & 0x5555555555555555;
    x = (x | (x >> 1))  & 0x3333333333333333;
    x = (x | (x >> 2))  & 0x0F0F0F0F0F0F0F0F;
    x = (x | (x >> 4))  & 0x00FF00FF00FF00FF;
    x = (x | (x >> 8))  & 0x0000FFFF0000FFFF;
    x = (x | (x >> 16)) & 0x00000000FFFFFFFF;
    return (uint32_t)x;
}

void d2xy_morton(uint64_t d, uint64_t &x, uint64_t &y)
{
    x = morton_1(d);
    y = morton_1(d >> 1);
}

不管位數如何,幼稚的代碼都是一樣的。 如果您不需要超快速位旋轉版本,則可以

uint32_t x;
uint32_t y;
uint64_t z = 0;

for (int i = 0; i < sizeof(x) * 8; i++)
{
  z |= (x & (uint64_t)1 << i) << i | (y & (uint64_t)1 << i) << (i + 1);
}

如果您需要更快的旋轉速度,那么此操作應該可行。 請注意,x和y必須是64位變量。

uint64_t x;
uint64_t y;
uint64_t z = 0;

x = (x | (x << 16)) & 0x0000FFFF0000FFFF;
x = (x | (x << 8)) & 0x00FF00FF00FF00FF;
x = (x | (x << 4)) & 0x0F0F0F0F0F0F0F0F;
x = (x | (x << 2)) & 0x3333333333333333;
x = (x | (x << 1)) & 0x5555555555555555;

y = (y | (y << 16)) & 0x0000FFFF0000FFFF;
y = (y | (y << 8)) & 0x00FF00FF00FF00FF;
y = (y | (y << 4)) & 0x0F0F0F0F0F0F0F0F;
y = (y | (y << 2)) & 0x3333333333333333;
y = (y | (y << 1)) & 0x5555555555555555;

z = x | (y << 1);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM