sw_engine/math: Enhance trigonometric functions.

Streamlining computations with floating-point operations in rotation
thereby improving 'thorvg' speed.

Also use the well-optimized posix math functions instead of
custom math.

Test on my local machine.

Lottie: -0.008s (0.073 -> 0.065)
Performance: -0.0013s (0.0154 -> 0.0141)
Binary: -323
This commit is contained in:
Hermet Park 2023-10-18 14:29:54 +09:00
parent 1b15233aeb
commit 81e3025ad9
2 changed files with 32 additions and 208 deletions

View file

@ -43,11 +43,16 @@ static double timeStamp()
#define SW_ANGLE_PI (180L << 16)
#define SW_ANGLE_2PI (SW_ANGLE_PI << 1)
#define SW_ANGLE_PI2 (SW_ANGLE_PI >> 1)
#define SW_ANGLE_PI4 (SW_ANGLE_PI >> 2)
using SwCoord = signed long;
using SwFixed = signed long long;
static inline float TO_FLOAT(SwCoord val)
{
return static_cast<float>(val) / 64.0f;
}
struct SwPoint
{
SwCoord x, y;
@ -92,6 +97,10 @@ struct SwPoint
else return false;
}
Point toPoint() const
{
return {TO_FLOAT(x), TO_FLOAT(y)};
}
};
struct SwSize

View file

@ -20,7 +20,7 @@
* SOFTWARE.
*/
#include <math.h>
#include "tvgMath.h"
#include "tvgSwCommon.h"
@ -28,173 +28,9 @@
/* Internal Class Implementation */
/************************************************************************/
//clz: count leading zeros
#if defined(_MSC_VER) && !defined(__clang__)
#include <intrin.h>
static uint32_t __inline _clz(uint32_t value)
{
unsigned long leadingZero = 0;
if (_BitScanReverse(&leadingZero, value)) return 31 - leadingZero;
else return 32;
}
#else
#define _clz(x) __builtin_clz((x))
#endif
constexpr SwFixed CORDIC_FACTOR = 0xDBD95B16UL; //the Cordic shrink factor 0.858785336480436 * 2^32
//this table was generated for SW_FT_PI = 180L << 16, i.e. degrees
constexpr static auto ATAN_MAX = 23;
constexpr static SwFixed ATAN_TBL[] = {
1740967L, 919879L, 466945L, 234379L, 117304L, 58666L, 29335L,
14668L, 7334L, 3667L, 1833L, 917L, 458L, 229L, 115L,
57L, 29L, 14L, 7L, 4L, 2L, 1L};
static inline SwCoord SATURATE(const SwCoord x)
static float TO_RADIAN(SwFixed angle)
{
return (x >> (sizeof(SwCoord) * 8 - 1));
}
static inline SwFixed PAD_ROUND(const SwFixed x, int32_t n)
{
return (((x) + ((n)/2)) & ~((n)-1));
}
static SwCoord _downscale(SwFixed x)
{
//multiply a give value by the CORDIC shrink factor
auto s = abs(x);
int64_t t = (s * static_cast<int64_t>(CORDIC_FACTOR)) + 0x100000000UL;
s = static_cast<SwFixed>(t >> 32);
if (x < 0) s = -s;
return s;
}
static int32_t _normalize(SwPoint& pt)
{
/* the highest bit in overflow-safe vector components
MSB of 0.858785336480436 * sqrt(0.5) * 2^30 */
constexpr auto SAFE_MSB = 29;
auto v = pt;
//High order bit(MSB)
int32_t shift = 31 - _clz(abs(v.x) | abs(v.y));
if (shift <= SAFE_MSB) {
shift = SAFE_MSB - shift;
pt.x = static_cast<SwCoord>((unsigned long)v.x << shift);
pt.y = static_cast<SwCoord>((unsigned long)v.y << shift);
} else {
shift -= SAFE_MSB;
pt.x = v.x >> shift;
pt.y = v.y >> shift;
shift = -shift;
}
return shift;
}
static void _polarize(SwPoint& pt)
{
auto v = pt;
SwFixed theta;
//Get the vector into [-PI/4, PI/4] sector
if (v.y > v.x) {
if (v.y > -v.x) {
auto tmp = v.y;
v.y = -v.x;
v.x = tmp;
theta = SW_ANGLE_PI2;
} else {
theta = v.y > 0 ? SW_ANGLE_PI : -SW_ANGLE_PI;
v.x = -v.x;
v.y = -v.y;
}
} else {
if (v.y < -v.x) {
theta = -SW_ANGLE_PI2;
auto tmp = -v.y;
v.y = v.x;
v.x = tmp;
} else {
theta = 0;
}
}
auto atan = ATAN_TBL;
uint32_t i;
SwFixed j;
//Pseudorotations. with right shifts
for (i = 1, j = 1; i < ATAN_MAX; j <<= 1, ++i) {
if (v.y > 0) {
auto tmp = v.x + ((v.y + j) >> i);
v.y = v.y - ((v.x + j) >> i);
v.x = tmp;
theta += *atan++;
} else {
auto tmp = v.x - ((v.y + j) >> i);
v.y = v.y + ((v.x + j) >> i);
v.x = tmp;
theta -= *atan++;
}
}
//round theta
if (theta >= 0) theta = PAD_ROUND(theta, 32);
else theta = -PAD_ROUND(-theta, 32);
pt.x = v.x;
pt.y = theta;
}
static void _rotate(SwPoint& pt, SwFixed theta)
{
SwFixed x = pt.x;
SwFixed y = pt.y;
//Rotate inside [-PI/4, PI/4] sector
while (theta < -SW_ANGLE_PI4) {
auto tmp = y;
y = -x;
x = tmp;
theta += SW_ANGLE_PI2;
}
while (theta > SW_ANGLE_PI4) {
auto tmp = -y;
y = x;
x = tmp;
theta -= SW_ANGLE_PI2;
}
auto atan = ATAN_TBL;
uint32_t i;
SwFixed j;
for (i = 1, j = 1; i < ATAN_MAX; j <<= 1, ++i) {
if (theta < 0) {
auto tmp = x + ((y + j) >> i);
y = y - ((x + j) >> i);
x = tmp;
theta += *atan++;
} else {
auto tmp = x - ((y + j) >> i);
y = y + ((x + j) >> i);
x = tmp;
theta -= *atan++;
}
}
pt.x = static_cast<SwCoord>(x);
pt.y = static_cast<SwCoord>(y);
return (float(angle) / 65536.0f) * (MATH_PI / 180.0f);
}
@ -320,77 +156,56 @@ int64_t mathMulDiv(int64_t a, int64_t b, int64_t c)
void mathRotate(SwPoint& pt, SwFixed angle)
{
if (angle == 0 || (pt.x == 0 && pt.y == 0)) return;
if (angle == 0 || pt.zero()) return;
auto v = pt;
auto shift = _normalize(v);
Point v = pt.toPoint();
auto theta = angle;
_rotate(v, theta);
auto radian = TO_RADIAN(angle);
auto cosv = cosf(radian);
auto sinv = sinf(radian);
v.x = _downscale(v.x);
v.y = _downscale(v.y);
if (shift > 0) {
auto half = static_cast<int32_t>(1L << (shift - 1));
pt.x = (v.x + half + SATURATE(v.x)) >> shift;
pt.y = (v.y + half + SATURATE(v.y)) >> shift;
} else {
shift = -shift;
pt.x = static_cast<SwCoord>((unsigned long)v.x << shift);
pt.y = static_cast<SwCoord>((unsigned long)v.y << shift);
}
pt.x = SwCoord(roundf((v.x * cosv - v.y * sinv) * 64.0f));
pt.y = SwCoord(roundf((v.x * sinv + v.y * cosv) * 64.0f));
}
SwFixed mathTan(SwFixed angle)
{
SwPoint v = {CORDIC_FACTOR >> 8, 0};
_rotate(v, angle);
return mathDivide(v.y, v.x);
if (angle == 0) return 0;
return SwFixed(tanf(TO_RADIAN(angle)) * 65536.0f);
}
SwFixed mathAtan(const SwPoint& pt)
{
if (pt.x == 0 && pt.y == 0) return 0;
auto v = pt;
_normalize(v);
_polarize(v);
return v.y;
if (pt.zero()) return 0;
return SwFixed(atan2f(TO_FLOAT(pt.y), TO_FLOAT(pt.x)) * (180.0f / MATH_PI) * 65536.0f);
}
SwFixed mathSin(SwFixed angle)
{
if (angle == 0) return 0;
return mathCos(SW_ANGLE_PI2 - angle);
}
SwFixed mathCos(SwFixed angle)
{
SwPoint v = {CORDIC_FACTOR >> 8, 0};
_rotate(v, angle);
return (v.x + 0x80L) >> 8;
return SwFixed(cosf(TO_RADIAN(angle)) * 65536.0f);
}
SwFixed mathLength(const SwPoint& pt)
{
auto v = pt;
if (pt.zero()) return 0;
//trivial case
if (v.x == 0) return abs(v.y);
if (v.y == 0) return abs(v.x);
if (pt.x == 0) return abs(pt.y);
if (pt.y == 0) return abs(pt.x);
//general case
auto shift = _normalize(v);
_polarize(v);
v.x = _downscale(v.x);
if (shift > 0) return (v.x + (static_cast<SwFixed>(1) << (shift -1))) >> shift;
return static_cast<SwFixed>((uint32_t)v.x << -shift);
auto v = pt.toPoint();
return static_cast<SwFixed>(sqrtf(v.x * v.x + v.y * v.y) / 64.0f);
}