diff --git a/glm/detail/func_integer.inl b/glm/detail/func_integer.inl index c53f70d4..d6e94e99 100644 --- a/glm/detail/func_integer.inl +++ b/glm/detail/func_integer.inl @@ -39,8 +39,15 @@ #endif//(GLM_ARCH != GLM_ARCH_PURE) #include -namespace glm +namespace glm{ +namespace detail { + GLM_FUNC_QUALIFIER int mask(int Bits) + { + return Bits >= 32 ? 0xffffffff : (static_cast(1) << Bits) - static_cast(1); + } +}//namespace detail + // uaddCarry GLM_FUNC_QUALIFIER uint uaddCarry(uint const & x, uint const & y, uint & Carry) { @@ -139,16 +146,8 @@ namespace glm { GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'bitfieldExtract' only accept integer inputs"); - int GenSize = int(sizeof(T)) << int(3); - - assert(Offset + Bits <= GenSize); - - vecType ShiftLeft(0); - if(Bits) - ShiftLeft = Value << static_cast(GenSize - (Bits + Offset)); - vecType const ShiftBack = ShiftLeft >> static_cast(GenSize - Bits); - - return ShiftBack; + int const Mask = detail::mask(Bits); + return (Value >> static_cast(Offset)) & static_cast(Mask); } // bitfieldInsert @@ -163,13 +162,7 @@ namespace glm { GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'bitfieldInsert' only accept integer values"); - if(Bits == 0) - return Base; - - vecType Mask(0); - for(int Bit = Offset; Bit < Offset + Bits; ++Bit) - Mask |= (static_cast(1) << Bit); - + T Mask = static_cast(detail::mask(Bits) << Offset); return (Base & ~Mask) | (Insert & Mask); } @@ -186,7 +179,6 @@ namespace glm GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'bitfieldReverse' only accept integer values"); vecType Result(0); - vecType const Null(0); T const BitSize = static_cast(sizeof(T) * 8); for(T i = 0; i < BitSize; ++i) { @@ -210,11 +202,8 @@ namespace glm GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'bitCount' only accept integer values"); vecType Count(0); - for(std::size_t i = 0; i < sizeof(T) * std::size_t(8); ++i) - { - if(v & (static_cast(1) << i)) - ++Count; - } + for(T i = 0, n = static_cast(sizeof(T) * 8); i < n; ++i) + Count += vecType((v >> i) & static_cast(1)); return Count; } diff --git a/glm/gtx/bit.hpp b/glm/gtx/bit.hpp index f358bcc4..b85e9388 100644 --- a/glm/gtx/bit.hpp +++ b/glm/gtx/bit.hpp @@ -41,6 +41,7 @@ // Dependencies #include "../detail/type_int.hpp" #include "../detail/setup.hpp" +#include "../detail/precision.hpp" #include #if(defined(GLM_MESSAGES) && !defined(GLM_EXT_INCLUDED)) @@ -54,8 +55,10 @@ namespace glm /// Build a mask of 'count' bits /// @see gtx_bit - template - GLM_FUNC_DECL genIType mask(genIType const & count); + GLM_FUNC_DECL int mask(int Bits); + + template class vecType> + GLM_FUNC_DECL vecType mask(vecType const & v); //! Find the highest bit set to 1 in a integer variable and return its value. /// @see gtx_bit diff --git a/glm/gtx/bit.inl b/glm/gtx/bit.inl index e5e420e1..c773c82f 100644 --- a/glm/gtx/bit.inl +++ b/glm/gtx/bit.inl @@ -12,16 +12,16 @@ namespace glm { - template - GLM_FUNC_QUALIFIER genIType mask - ( - genIType const & count - ) + GLM_FUNC_QUALIFIER int mask(int Bits) { - return ((genIType(1) << (count)) - genIType(1)); + return Bits >= sizeof(Bits) * 8 ? ~static_cast(0) : (static_cast(1) << Bits) - static_cast(1); } - VECTORIZE_VEC(mask) + template class vecType> + GLM_FUNC_QUALIFIER vecType mask(vecType const & v) + { + return detail::functor1::call(mask, v); + } // highestBitValue template diff --git a/readme.txt b/readme.txt index 12c32039..43392cb5 100644 --- a/readme.txt +++ b/readme.txt @@ -76,6 +76,7 @@ GLM 0.9.6.0: 2014-XX-XX - Added GLM_FORCE_NO_CTOR_INIT - Added 'uninitialize' to explicitly not initialize a GLM type - Added not function (from GLSL specification) on VC12 +- Optimized bitfield operations ================================================================================ GLM 0.9.5.4: 2014-06-21 diff --git a/test/core/core_func_integer.cpp b/test/core/core_func_integer.cpp index fedcbd92..adae41ea 100644 --- a/test/core/core_func_integer.cpp +++ b/test/core/core_func_integer.cpp @@ -10,6 +10,8 @@ #include #include #include +#include +#include enum result { @@ -29,18 +31,22 @@ namespace bitfieldInsert sizeType Offset; sizeType Bits; genType Return; - result Result; }; typedef type typeU32; typeU32 const Data32[] = { - {0xffffffff, 8,24, 0xffffff00, SUCCESS}, + {0xff000000, 0x0000ff00, 8, 8, 0xff00ff00}, + {0xffff0000, 0x0000ffff, 16, 16, 0x00000000}, + {0x0000ffff, 0xffff0000, 16, 16, 0xffffffff}, + {0x00000000, 0xffffffff, 0, 32, 0xffffffff}, + {0x00000000, 0xffffffff, 0, 0, 0x00000000} }; int test() { + int Error = 0; glm::uint count = sizeof(Data32) / sizeof(typeU32); for(glm::uint i = 0; i < count; ++i) @@ -50,19 +56,11 @@ namespace bitfieldInsert Data32[i].Insert, Data32[i].Offset, Data32[i].Bits); - - bool Compare = Data32[i].Return == Return; - - if(Data32[i].Result == SUCCESS && Compare) - continue; - else if(Data32[i].Result == FAIL && !Compare) - continue; - - std::cout << "glm::bitfieldInsert test fail on test " << i << std::endl; - return 1; + + Error += Data32[i].Return == Return ? 0 : 1; } - return 0; + return Error; } }//bitfieldInsert @@ -72,8 +70,8 @@ namespace bitfieldExtract struct type { genType Value; - sizeType BitFirst; - sizeType BitCount; + sizeType Offset; + sizeType Bits; genType Return; result Result; }; @@ -82,9 +80,9 @@ namespace bitfieldExtract typeU32 const Data32[] = { + {0xffffffff, 0,32, 0xffffffff, SUCCESS}, {0xffffffff, 8, 0, 0x00000000, SUCCESS}, {0x00000000, 0,32, 0x00000000, SUCCESS}, - {0xffffffff, 0,32, 0xffffffff, SUCCESS}, {0x0f0f0f0f, 0,32, 0x0f0f0f0f, SUCCESS}, {0x00000000, 8, 0, 0x00000000, SUCCESS}, {0x80000000,31, 1, 0x00000001, SUCCESS}, @@ -106,27 +104,28 @@ namespace bitfieldExtract int test() { + int Error = 0; + glm::uint count = sizeof(Data32) / sizeof(typeU32); - + for(glm::uint i = 0; i < count; ++i) { glm::uint Return = glm::bitfieldExtract( Data32[i].Value, - Data32[i].BitFirst, - Data32[i].BitCount); + Data32[i].Offset, + Data32[i].Bits); bool Compare = Data32[i].Return == Return; - + if(Data32[i].Result == SUCCESS && Compare) continue; else if(Data32[i].Result == FAIL && !Compare) continue; - - std::cout << "glm::bitfieldExtract test fail on test " << i << std::endl; - return 1; + + Error += 1; } - - return 0; + + return Error; } }//extractField @@ -517,6 +516,115 @@ namespace imulExtended } }//namespace imulExtended +namespace bitCount +{ + template + struct type + { + genType Value; + genType Return; + }; + + type const DataI32[] = + { + {0x00000001, 1}, + {0x00000003, 2}, + {0x00000002, 1}, + {0xffffffff, 32}, + {0x00000000, 0} + }; + + template + int bitCount_if(T v) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'bitCount' only accept integer values"); + + int Count(0); + for(T i = 0, n = static_cast(sizeof(T) * 8); i < n; ++i) + { + if(v & static_cast(1 << i)) + ++Count; + } + return Count; + } + + template + int bitCount_vec(T v) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'bitCount' only accept integer values"); + + int Count(0); + for(T i = 0, n = static_cast(sizeof(T) * 8); i < n; ++i) + { + Count += static_cast((v >> i) & static_cast(1)); + } + return Count; + } + + int perf() + { + int Error(0); + + std::size_t Size = 10000000; + + std::clock_t TimestampsA = std::clock(); + + { + std::vector v; + v.resize(Size); + + for(std::size_t i = 0, n = v.size(); i < n; ++i) + v[i] = bitCount_if(i); + } + + std::clock_t TimestampsB = std::clock(); + + { + std::vector v; + v.resize(Size); + + for(std::size_t i = 0, n = v.size(); i < n; ++i) + v[i] = bitCount_vec(i); + } + + std::clock_t TimestampsC = std::clock(); + + { + std::vector v; + v.resize(Size); + + for(std::size_t i = 0, n = v.size(); i < n; ++i) + v[i] = glm::bitCount(i); + } + + std::clock_t TimestampsD = std::clock(); + + std::clock_t TimeIf = TimestampsB - TimestampsA; + std::clock_t TimeVec = TimestampsC - TimestampsB; + std::clock_t TimeDefault = TimestampsD - TimestampsC; + + printf("TimeIf %d\n", TimeIf); + printf("TimeVec %d\n", TimeVec); + printf("TimeDefault %d\n", TimeDefault); + + return Error; + } + + int test() + { + int Error(0); + + for(std::size_t i = 0, n = sizeof(DataI32) / sizeof(type); i < n; ++i) + { + int Result = glm::bitCount(DataI32[i].Value); + Error += DataI32[i].Return == Result ? 0 : 1; + assert(!Error); + } + + return Error; + } +}//bitCount + int main() { int Error = 0; @@ -530,6 +638,8 @@ int main() Error += ::bitfieldInsert::test(); Error += ::bitfieldExtract::test(); Error += ::bitfieldReverse::test(); + Error += ::bitCount::test(); + Error += ::bitCount::perf(); Error += ::findMSB::test(); Error += ::findLSB::test(); diff --git a/test/gtx/gtx_bit.cpp b/test/gtx/gtx_bit.cpp index a696c88b..94cea9c6 100644 --- a/test/gtx/gtx_bit.cpp +++ b/test/gtx/gtx_bit.cpp @@ -501,10 +501,71 @@ namespace bitfieldInterleave4 } } +namespace mask +{ + inline int mask_mix(int Bits) + { + return Bits >= 32 ? 0xffffffff : (static_cast(1) << Bits) - static_cast(1); + } + + inline int mask_loop(int Bits) + { + int Mask = 0; + for(int Bit = 0; Bit < Bits; ++Bit) + Mask |= (static_cast(1) << Bit); + return Mask; + } + + int perf() + { + int const Count = 100000000; + + std::clock_t Timestamp1 = std::clock(); + + { + std::vector Mask; + Mask.resize(Count); + for(int i = 0; i < Count; ++i) + Mask[i] = mask_mix(i % 32); + } + + std::clock_t Timestamp2 = std::clock(); + + { + std::vector Mask; + Mask.resize(Count); + for(int i = 0; i < Count; ++i) + Mask[i] = mask_loop(i % 32); + } + + std::clock_t Timestamp3 = std::clock(); + + { + std::vector Mask; + Mask.resize(Count); + for(int i = 0; i < Count; ++i) + Mask[i] = glm::mask(i % 32); + } + + std::clock_t Timestamp4 = std::clock(); + + std::clock_t TimeMix = Timestamp2 - Timestamp1; + std::clock_t TimeLoop = Timestamp3 - Timestamp2; + std::clock_t TimeDefault = Timestamp4 - Timestamp3; + + printf("mask[mix]: %d\n", TimeMix); + printf("mask[loop]: %d\n", TimeLoop); + printf("mask[default]: %d\n", TimeDefault); + + return TimeDefault < TimeLoop ? 0 : 1; + } +}//namespace mask + int main() { int Error(0); + Error += ::mask::perf(); Error += ::bitfieldInterleave3::test(); Error += ::bitfieldInterleave4::test(); Error += ::bitfieldInterleave::test();