/* enoki/morton.h -- Morton/Z-order curve encoding and decoding routines Enoki is a C++ template library that enables transparent vectorization of numerical kernels using SIMD instruction sets available on current processor architectures. Copyright (c) 2019 Wenzel Jakob Includes contributions by Sebastien Speierer All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. */ #pragma once #include #if defined(_MSC_VER) # pragma warning (push) # pragma warning (disable: 4310) // cast truncates constant value #endif NAMESPACE_BEGIN(enoki) NAMESPACE_BEGIN(detail) /// Generate bit masks for the functions \ref scatter_bits() and \ref gather_bits() template constexpr Value morton_magic(size_t dim, size_t level) { size_t n_bits = sizeof(Value) * 8; size_t max_block_size = n_bits / dim; size_t block_size = std::min(size_t(1) << (level - 1), max_block_size); size_t count = 0; Value mask = Value(1) << (n_bits - 1), value = Value(0); for (size_t i = 0; i < n_bits; ++i) { value >>= 1; if (count < max_block_size && (i / block_size) % dim == 0) { count++; value |= mask; } } return value; } /// Bit scatter function. \c Dimension defines the final distance between two output bits template = 0> ENOKI_INLINE Value scatter_bits(Value x) { return x; } template )> = 0> ENOKI_INLINE Value scatter_bits(Value x) { using Scalar = scalar_t; constexpr Scalar magic = morton_magic(Dimension, Level); constexpr size_t shift_maybe = (1 << (Level - 1)) * (Dimension - 1); constexpr size_t shift = (shift_maybe < sizeof(Scalar) * 8) ? shift_maybe : 0; if constexpr (shift != 0) x |= sl(x); x &= magic; return scatter_bits(x); } template = 0> ENOKI_INLINE Value gather_bits(Value x) { return x; } /// Bit gather function. \c Dimension defines the final distance between two input bits template )> = 0> ENOKI_INLINE Value gather_bits(Value x) { using Scalar = scalar_t; constexpr size_t ilevel = clog2i(sizeof(Value) * 8) - Level + 1; constexpr Scalar magic = morton_magic(Dimension, ilevel); constexpr size_t shift_maybe = (1 << (ilevel - 1)) * (Dimension - 1); constexpr size_t shift = (shift_maybe < sizeof(Scalar) * 8) ? shift_maybe : 0; x &= magic; if constexpr (shift != 0) x |= sr(x); return gather_bits(x); } #if defined(ENOKI_X86_AVX2) && defined(ENOKI_X86_64) template > = 0> ENOKI_INLINE Value scatter_bits(Value x) { constexpr Value magic = morton_magic(Dimension, 1); if constexpr (sizeof(Value) <= 4) return Value(_pdep_u32((uint32_t) x, (uint32_t) magic)); else return Value(_pdep_u64((uint64_t) x, (uint64_t) magic)); } template > = 0> ENOKI_INLINE Value gather_bits(Value x) { constexpr Value magic = morton_magic(Dimension, 1); if constexpr (sizeof(Value) <= 4) return Value(_pext_u32((uint32_t) x, (uint32_t) magic)); else return Value(_pext_u64((uint64_t) x, (uint64_t) magic)); } #endif template = 0> ENOKI_INLINE void morton_decode_helper(value_t value, Array &out) { out.coeff(0) = gather_bits(value); } template - 1, enable_if_t = 0> ENOKI_INLINE void morton_decode_helper(value_t value, Array &out) { out.coeff(Index) = gather_bits(sr(value)); morton_decode_helper(value, out); } NAMESPACE_END(detail) /// Convert a N-dimensional integer array into the Morton/Z-order curve encoding template , enable_if_t = 0> ENOKI_INLINE Return morton_encode(Array a) { return detail::scatter_bits(a.coeff(0)); } /// Convert a N-dimensional integer array into the Morton/Z-order curve encoding template - 1, typename Return = value_t, enable_if_t = 0> ENOKI_INLINE Return morton_encode(Array a) { static_assert(std::is_unsigned_v>, "morton_encode() requires unsigned arguments"); return sl(detail::scatter_bits(a.coeff(Index))) | morton_encode(a); } /// Convert Morton/Z-order curve encoding into a N-dimensional integer array template > ENOKI_INLINE Array morton_decode(Value value) { static_assert(std::is_unsigned_v>, "morton_decode() requires unsigned arguments"); Array result; detail::morton_decode_helper(value, result); return result; } NAMESPACE_END(enoki) #if defined(_MSC_VER) # pragma warning (pop) #endif