/** * @file llvector4a.cpp * @brief SIMD vector implementation * * $LicenseInfo:firstyear=2010&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #include "llmath.h" #include "llquantize.h" extern const LLQuad F_ZERO_4A = { 0, 0, 0, 0 }; extern const LLQuad F_APPROXIMATELY_ZERO_4A = { F_APPROXIMATELY_ZERO, F_APPROXIMATELY_ZERO, F_APPROXIMATELY_ZERO, F_APPROXIMATELY_ZERO }; extern const LLVector4a LL_V4A_ZERO = reinterpret_cast<const LLVector4a&> ( F_ZERO_4A ); extern const LLVector4a LL_V4A_EPSILON = reinterpret_cast<const LLVector4a&> ( F_APPROXIMATELY_ZERO_4A ); /*static */void LLVector4a::memcpyNonAliased16(F32* __restrict dst, const F32* __restrict src, size_t bytes) { assert(src != NULL); assert(dst != NULL); assert(bytes > 0); assert((bytes % sizeof(F32))== 0); F32* end = dst + (bytes / sizeof(F32) ); if (bytes > 64) { F32* begin_64 = LL_NEXT_ALIGNED_ADDRESS_64(dst); //at least 64 (16*4) bytes before the end of the destination, switch to 16 byte copies F32* end_64 = end-16; _mm_prefetch((char*)begin_64, _MM_HINT_NTA); _mm_prefetch((char*)begin_64 + 64, _MM_HINT_NTA); _mm_prefetch((char*)begin_64 + 128, _MM_HINT_NTA); _mm_prefetch((char*)begin_64 + 192, _MM_HINT_NTA); while (dst < begin_64) { copy4a(dst, src); dst += 4; src += 4; } while (dst < end_64) { _mm_prefetch((char*)src + 512, _MM_HINT_NTA); _mm_prefetch((char*)dst + 512, _MM_HINT_NTA); copy4a(dst, src); copy4a(dst+4, src+4); copy4a(dst+8, src+8); copy4a(dst+12, src+12); dst += 16; src += 16; } } while (dst < end) { copy4a(dst, src); dst += 4; src += 4; } } void LLVector4a::setRotated( const LLRotation& rot, const LLVector4a& vec ) { const LLVector4a col0 = rot.getColumn(0); const LLVector4a col1 = rot.getColumn(1); const LLVector4a col2 = rot.getColumn(2); LLVector4a result = _mm_load_ss( vec.getF32ptr() ); result.splat<0>( result ); result.mul( col0 ); { LLVector4a yyyy = _mm_load_ss( vec.getF32ptr() + 1 ); yyyy.splat<0>( yyyy ); yyyy.mul( col1 ); result.add( yyyy ); } { LLVector4a zzzz = _mm_load_ss( vec.getF32ptr() + 2 ); zzzz.splat<0>( zzzz ); zzzz.mul( col2 ); result.add( zzzz ); } *this = result; } void LLVector4a::setRotated( const LLQuaternion2& quat, const LLVector4a& vec ) { const LLVector4a& quatVec = quat.getVector4a(); LLVector4a temp; temp.setCross3(quatVec, vec); temp.add( temp ); const LLVector4a realPart( quatVec.getScalarAt<3>() ); LLVector4a tempTimesReal; tempTimesReal.setMul( temp, realPart ); mQ = vec; add( tempTimesReal ); LLVector4a imagCrossTemp; imagCrossTemp.setCross3( quatVec, temp ); add(imagCrossTemp); } void LLVector4a::quantize8( const LLVector4a& low, const LLVector4a& high ) { LLVector4a val(mQ); LLVector4a delta; delta.setSub( high, low ); { val.clamp(low, high); val.sub(low); // 8-bit quantization means we can do with just 12 bits of reciprocal accuracy const LLVector4a oneOverDelta = _mm_rcp_ps(delta.mQ); // { // static LL_ALIGN_16( const F32 F_TWO_4A[4] ) = { 2.f, 2.f, 2.f, 2.f }; // LLVector4a two; two.load4a( F_TWO_4A ); // // // Here we use _mm_rcp_ps plus one round of newton-raphson // // We wish to find 'x' such that x = 1/delta // // As a first approximation, we take x0 = _mm_rcp_ps(delta) // // Then x1 = 2 * x0 - a * x0^2 or x1 = x0 * ( 2 - a * x0 ) // // See Intel AP-803 http://ompf.org/!/Intel_application_note_AP-803.pdf // const LLVector4a recipApprox = _mm_rcp_ps(delta.mQ); // oneOverDelta.setMul( delta, recipApprox ); // oneOverDelta.setSub( two, oneOverDelta ); // oneOverDelta.mul( recipApprox ); // } val.mul(oneOverDelta); val.mul(*reinterpret_cast<const LLVector4a*>(F_U8MAX_4A)); } val = _mm_cvtepi32_ps(_mm_cvtps_epi32( val.mQ )); { val.mul(*reinterpret_cast<const LLVector4a*>(F_OOU8MAX_4A)); val.mul(delta); val.add(low); } { LLVector4a maxError; maxError.setMul(delta, *reinterpret_cast<const LLVector4a*>(F_OOU8MAX_4A)); LLVector4a absVal; absVal.setAbs( val ); setSelectWithMask( absVal.lessThan( maxError ), F_ZERO_4A, val ); } } void LLVector4a::quantize16( const LLVector4a& low, const LLVector4a& high ) { LLVector4a val(mQ); LLVector4a delta; delta.setSub( high, low ); { val.clamp(low, high); val.sub(low); // 16-bit quantization means we need a round of Newton-Raphson LLVector4a oneOverDelta; { static LL_ALIGN_16( const F32 F_TWO_4A[4] ) = { 2.f, 2.f, 2.f, 2.f }; LLVector4a two; two.load4a( F_TWO_4A ); // Here we use _mm_rcp_ps plus one round of newton-raphson // We wish to find 'x' such that x = 1/delta // As a first approximation, we take x0 = _mm_rcp_ps(delta) // Then x1 = 2 * x0 - a * x0^2 or x1 = x0 * ( 2 - a * x0 ) // See Intel AP-803 http://ompf.org/!/Intel_application_note_AP-803.pdf const LLVector4a recipApprox = _mm_rcp_ps(delta.mQ); oneOverDelta.setMul( delta, recipApprox ); oneOverDelta.setSub( two, oneOverDelta ); oneOverDelta.mul( recipApprox ); } val.mul(oneOverDelta); val.mul(*reinterpret_cast<const LLVector4a*>(F_U16MAX_4A)); } val = _mm_cvtepi32_ps(_mm_cvtps_epi32( val.mQ )); { val.mul(*reinterpret_cast<const LLVector4a*>(F_OOU16MAX_4A)); val.mul(delta); val.add(low); } { LLVector4a maxError; maxError.setMul(delta, *reinterpret_cast<const LLVector4a*>(F_OOU16MAX_4A)); LLVector4a absVal; absVal.setAbs( val ); setSelectWithMask( absVal.lessThan( maxError ), F_ZERO_4A, val ); } }