Compare commits
2
Commits
1408c6ac83
...
8388f448f3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8388f448f3 | ||
|
|
4db0f654ed |
@@ -15,7 +15,7 @@ set(DV_CORE_MATH_INCLUDE_FILES
|
|||||||
include/math/dvc_matrix3.h
|
include/math/dvc_matrix3.h
|
||||||
include/math/dvc_matrix4.h
|
include/math/dvc_matrix4.h
|
||||||
include/math/dvc_math_util.h
|
include/math/dvc_math_util.h
|
||||||
include/math/dvc_fixmath.h
|
include/math/dvc_fix_point.h
|
||||||
)
|
)
|
||||||
source_group("include/math" FILES ${DV_CORE_MATH_INCLUDE_FILES})
|
source_group("include/math" FILES ${DV_CORE_MATH_INCLUDE_FILES})
|
||||||
|
|
||||||
@@ -26,6 +26,7 @@ set(DV_CORE_MATH_SOURCE_FILES
|
|||||||
source/math/dvc_matrix3.cpp
|
source/math/dvc_matrix3.cpp
|
||||||
source/math/dvc_matrix4.cpp
|
source/math/dvc_matrix4.cpp
|
||||||
source/math/dvc_math_util.cpp
|
source/math/dvc_math_util.cpp
|
||||||
|
source/math/dvc_fix_point.cpp
|
||||||
)
|
)
|
||||||
source_group("source/math" FILES ${DV_CORE_MATH_SOURCE_FILES})
|
source_group("source/math" FILES ${DV_CORE_MATH_SOURCE_FILES})
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,456 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "dvc_config.h"
|
||||||
|
|
||||||
|
DV_CORE_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
class ifloat32
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static constexpr int32_t kShift = 16;
|
||||||
|
static constexpr float32_t kFloatScale = static_cast<float32_t>(1<<kShift); //65536.0
|
||||||
|
static constexpr float32_t kFloatInvScale = 1.f / kFloatScale;
|
||||||
|
|
||||||
|
static constexpr int32_t kZero_s1516 = 0;
|
||||||
|
static constexpr int32_t kOne_s1516 = 1 << kShift;
|
||||||
|
static constexpr int32_t kTwo_s1516 = 2 << kShift;
|
||||||
|
static constexpr int32_t kThree_s1516 = 3 << kShift;
|
||||||
|
static constexpr int32_t kFour_s1516 = 4 << kShift;
|
||||||
|
static constexpr int32_t kHalf_s1516 = kOne_s1516 >> 1;
|
||||||
|
static constexpr int32_t kPI_s1516 = 0x3243f;//(int32_t)(M_PI * kFloatScale);
|
||||||
|
static constexpr int32_t kPi2_s1516 = 0x6487e;
|
||||||
|
static constexpr int32_t kPiHalf_s1516 = 0x1921f;
|
||||||
|
static constexpr int32_t kE_s1516 = 0x2b7e1;
|
||||||
|
|
||||||
|
static constexpr int32_t kNegOne_s1516 = -1 << kShift;
|
||||||
|
|
||||||
|
static constexpr int32_t kMax_s1516 = 0x7fffff80; //(int32_t)(nextafter(INT32_MAX/kFloatScale, 0.f)*kFloatScale);
|
||||||
|
static constexpr int32_t kMax_int32 = kMax_s1516 >> kShift;
|
||||||
|
static constexpr float32_t kMax_float = (float32_t)kMax_s1516 * kFloatInvScale;
|
||||||
|
|
||||||
|
static constexpr int32_t kMin_s1516 = 0x80000000; // (int32_t)(nextafter(-INT32_MAX / ifloat32::kFloatScale, -FLT_MAX) * ifloat32::kFloatScale)
|
||||||
|
static constexpr int32_t kMin_int32 = kMin_s1516 >> kShift;
|
||||||
|
static constexpr float32_t kMin_float = (float32_t)kMin_s1516 * kFloatInvScale;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ifloat32() : s1516(0) {}
|
||||||
|
ifloat32(const ifloat32& other) : s1516(other.s1516) {}
|
||||||
|
|
||||||
|
//Converts a float to a fixed-point value.
|
||||||
|
ifloat32(float32_t f) {
|
||||||
|
s1516 = static_cast<int32_t>(f * kFloatScale);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Converts a double to a fixed-point value.
|
||||||
|
ifloat32(float64_t f) {
|
||||||
|
s1516 = static_cast<int32_t>(f * kFloatScale);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Converts an integer to a fixed-point value.
|
||||||
|
ifloat32(int32_t n) {
|
||||||
|
s1516 = static_cast<int32_t>(n << kShift);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
//Converts into a float.
|
||||||
|
float32_t to_float() const {
|
||||||
|
return static_cast<float32_t>(s1516 * kFloatInvScale);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Converts a fixed-point value into an integer by rounding it down to nearest integer. example: 3.94 -> 3, -2.1 -> -3
|
||||||
|
int32_t to_int32() const {
|
||||||
|
return static_cast<int32_t>(s1516 >> kShift);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline ifloat32& operator = (const ifloat32& other) {
|
||||||
|
s1516 = other.s1516;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool operator == (const ifloat32& other) const {
|
||||||
|
return (s1516 == other.s1516);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool operator != (const ifloat32& other) const {
|
||||||
|
return (s1516 != other.s1516);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool operator < (const ifloat32& other) const {
|
||||||
|
return (s1516 < other.s1516);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool operator > (const ifloat32& other) const {
|
||||||
|
return (s1516 > other.s1516);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline ifloat32& operator += (const ifloat32 other) {
|
||||||
|
s1516 += other.s1516;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ifloat32& operator -= (const ifloat32 other) {
|
||||||
|
s1516 -= other.s1516;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
//Adds the two FP numbers together.
|
||||||
|
inline friend ifloat32 operator + (const ifloat32& l, const ifloat32& r) {
|
||||||
|
int64_t t = static_cast<int64_t>(l.s1516) + static_cast<int64_t>(r.s1516);
|
||||||
|
|
||||||
|
ifloat32 ret;
|
||||||
|
ret.s1516 = static_cast<int32_t>(t);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Subtracts the two FP numbers from each other.
|
||||||
|
inline friend ifloat32 operator - (const ifloat32& l, const ifloat32& r) {
|
||||||
|
int64_t t = static_cast<int64_t>(l.s1516) - static_cast<int64_t>(r.s1516);
|
||||||
|
|
||||||
|
ifloat32 ret;
|
||||||
|
ret.s1516 = t;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline friend ifloat32 operator * (const ifloat32& a, const float32_t& b) {
|
||||||
|
int64_t t = static_cast<int64_t>(a.s1516) * static_cast<int64_t>(b * kFloatScale);
|
||||||
|
|
||||||
|
t = t >> kShift;
|
||||||
|
|
||||||
|
ifloat32 ret;
|
||||||
|
ret.s1516 = t;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline friend ifloat32 operator * (const ifloat32& a, const int32_t& b) {
|
||||||
|
ifloat32 ret;
|
||||||
|
ret.s1516 = a.s1516 * b;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Multiplies two FP values together.
|
||||||
|
inline friend ifloat32 operator * (const ifloat32& a, const ifloat32& b) {
|
||||||
|
int64_t t = static_cast<int64_t>(a.s1516) * static_cast<int64_t>(b.s1516);
|
||||||
|
|
||||||
|
t = t >> kShift;
|
||||||
|
|
||||||
|
ifloat32 ret;
|
||||||
|
ret.s1516 = static_cast<int32_t>(t);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline friend ifloat32 operator / (const ifloat32& a, const ifloat32& b) {
|
||||||
|
// pre-multiply by the base
|
||||||
|
int64_t t = static_cast<int64_t>(a.s1516) << kShift;
|
||||||
|
t /= b.s1516;
|
||||||
|
|
||||||
|
ifloat32 ret;
|
||||||
|
ret.s1516 = static_cast<int32_t>(t);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
int32_t s1516;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ifloat64
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static constexpr int32_t kShift = 32;
|
||||||
|
static constexpr float32_t kFloatScale = static_cast<float32_t>(INT64_C(1) << kShift); //4294967296.0
|
||||||
|
static constexpr float32_t kFloatInvScale = 1.0f / kFloatScale;
|
||||||
|
static constexpr int64_t kFractionMask = (INT64_C(1) << kShift) - 1; // Space before INT64_C(1) needed because of hacky C++ code generator
|
||||||
|
|
||||||
|
//static_cast<int64_t>(nextafter(INT64_MAX / iFloat64::kFloatScale, 0.0) * iFloat64::kFloatScale);
|
||||||
|
static constexpr int64_t kMax_s3132 = INT64_C(0x7ffffffffffffc00);
|
||||||
|
static constexpr int32_t kMax_int32 = static_cast<int32_t>(kMax_s3132 >> kShift);
|
||||||
|
static constexpr float32_t kMax_float = 2.14748352e+09f; //nextafter(static_cast<float32_t>(kS3132_Max * kFloatInvScale), 0.0f);
|
||||||
|
|
||||||
|
//(int32_t)(nextafter(-INT64_MAX / iFloat64::kFloatScale, -DBL_MAX) * iFloat64::kFloatScale);
|
||||||
|
static constexpr int64_t kMin_s3132 = INT64_C(0x8000000000000000);
|
||||||
|
static constexpr int32_t kMin_int32 = static_cast<int32_t>(kMin_s3132 >> kShift);
|
||||||
|
static constexpr float32_t kMin_float = static_cast<float64_t>(kMin_s3132 * kFloatInvScale);
|
||||||
|
|
||||||
|
// special value
|
||||||
|
static DV_CORE_API const ifloat64 kMax;
|
||||||
|
static DV_CORE_API const ifloat64 kMin;
|
||||||
|
static DV_CORE_API const ifloat64 kZero;
|
||||||
|
static DV_CORE_API const ifloat64 kOne;
|
||||||
|
static DV_CORE_API const ifloat64 kTwo;
|
||||||
|
static DV_CORE_API const ifloat64 kThree;
|
||||||
|
static DV_CORE_API const ifloat64 kFour;
|
||||||
|
static DV_CORE_API const ifloat64 kHalf;
|
||||||
|
static DV_CORE_API const ifloat64 kPi;
|
||||||
|
static DV_CORE_API const ifloat64 kPi2;
|
||||||
|
static DV_CORE_API const ifloat64 kPiHalf;
|
||||||
|
static DV_CORE_API const ifloat64 kE;
|
||||||
|
static DV_CORE_API const ifloat64 kNegOne;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ifloat64() : s3132(0) {}
|
||||||
|
ifloat64(const ifloat64& other) : s3132(other.s3132) {}
|
||||||
|
|
||||||
|
//Converts a float to a fixed-point value.
|
||||||
|
ifloat64(float32_t f) {
|
||||||
|
s3132 = static_cast<int64_t>(std::round(f * kFloatScale));
|
||||||
|
}
|
||||||
|
|
||||||
|
//Converts a double to a fixed-point value.
|
||||||
|
ifloat64(float64_t f) {
|
||||||
|
s3132 = static_cast<int64_t>(std::round(f * kFloatScale));
|
||||||
|
}
|
||||||
|
|
||||||
|
//Converts an integer to a fixed-point value.
|
||||||
|
ifloat64(int32_t n) {
|
||||||
|
s3132 = static_cast<int64_t>(n) << kShift;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ifloat64(int64_t _s3132) {
|
||||||
|
s3132 = _s3132;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
//Converts into a float.
|
||||||
|
float32_t to_float() const {
|
||||||
|
return static_cast<float32_t>(s3132 * kFloatInvScale);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Converts a fixed-point value into an integer by rounding it down to nearest integer. example: 3.94 -> 3, -2.1 -> -3
|
||||||
|
int32_t to_int32() const {
|
||||||
|
return static_cast<int32_t>(s3132 >> kShift);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline const ifloat64& operator + () const {
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ifloat64 operator - () const {
|
||||||
|
return ifloat64(-s3132);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ifloat64& operator = (const ifloat64& other) {
|
||||||
|
s3132 = other.s3132;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool operator == (const ifloat64& other) const {
|
||||||
|
return (s3132 == other.s3132);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool operator != (const ifloat64& other) const {
|
||||||
|
return (s3132 != other.s3132);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool operator < (const ifloat64& other) const {
|
||||||
|
return (s3132 < other.s3132);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool operator > (const ifloat64& other) const {
|
||||||
|
return (s3132 > other.s3132);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline ifloat64& operator += (const ifloat64 other) {
|
||||||
|
s3132 += other.s3132;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ifloat64& operator -= (const ifloat64 other) {
|
||||||
|
s3132 -= other.s3132;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
//Adds the two FP numbers together.
|
||||||
|
inline friend ifloat64 operator + (const ifloat64& a, const ifloat64& b) {
|
||||||
|
ifloat64 ret;
|
||||||
|
ret.s3132 = a.s3132 + b.s3132;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Subtracts the two FP numbers from each other.
|
||||||
|
inline friend ifloat64 operator - (const ifloat64& a, const ifloat64& b) {
|
||||||
|
ifloat64 ret;
|
||||||
|
ret.s3132 = a.s3132 - b.s3132;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline friend ifloat64 operator * (const ifloat64& a, const int32_t& b) {
|
||||||
|
ifloat64 ret;
|
||||||
|
ret.s3132 = a.s3132 * b;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline friend ifloat64 operator * (const ifloat64& a, const float32_t& b) {
|
||||||
|
ifloat64 ret;
|
||||||
|
ret.s3132 = _safeMulti(a.s3132, static_cast<int64_t>(b * kFloatScale));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Multiplies two FP values together.
|
||||||
|
inline friend ifloat64 operator * (const ifloat64& a, const ifloat64& b) {
|
||||||
|
ifloat64 ret;
|
||||||
|
ret.s3132 = _safeMulti(a.s3132, b.s3132);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline friend ifloat64 operator / (const ifloat64& a, const ifloat64& b) {
|
||||||
|
// From http://www.hackersdelight.org/hdcodetxt/divlu.c.txt
|
||||||
|
|
||||||
|
int64_t sign_dif = a.s3132 ^ b.s3132;
|
||||||
|
|
||||||
|
static const uint64_t B = INT64_C(0x100000000); // Number base (32 bits)
|
||||||
|
|
||||||
|
uint64_t abs_a = (uint64_t)((a.s3132 < 0) ? -a.s3132 : a.s3132);
|
||||||
|
uint64_t u1 = abs_a >> 32;
|
||||||
|
uint64_t u0 = abs_a << 32;
|
||||||
|
uint64_t v = (uint64_t)((b.s3132 < 0) ? -b.s3132 : b.s3132);
|
||||||
|
|
||||||
|
// Overflow?
|
||||||
|
if (u1 >= v)
|
||||||
|
{
|
||||||
|
//invalid number
|
||||||
|
ifloat64 ret;
|
||||||
|
ret.s3132 = 0x7fffffffffffffff;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shift amount for norm
|
||||||
|
int32_t s = _nlz(v); // 0 <= s <= 63
|
||||||
|
v = v << s; // Normalize the divisor
|
||||||
|
uint64_t vn1 = v >> 32; // Break the divisor into two 32-bit digits
|
||||||
|
uint64_t vn0 = v & INT64_C(0xffffffff);
|
||||||
|
|
||||||
|
uint64_t un32 = (u1 << s) | (u0 >> (64 - s)) & (uint64_t)((int64_t)-s >> 63);
|
||||||
|
uint64_t un10 = u0 << s; // Shift dividend left
|
||||||
|
|
||||||
|
uint64_t un1 = un10 >> 32; // Break the right half of dividend into two digits
|
||||||
|
uint64_t un0 = un10 & INT64_C(0xffffffff);
|
||||||
|
|
||||||
|
// Compute the first quotient digit, q1
|
||||||
|
uint64_t q1 = un32 / vn1;
|
||||||
|
uint64_t rhat = un32 - q1 * vn1;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if ((q1 >= B) || ((q1 * vn0) > (B * rhat + un1)))
|
||||||
|
{
|
||||||
|
q1 = q1 - 1;
|
||||||
|
rhat = rhat + vn1;
|
||||||
|
}
|
||||||
|
else break;
|
||||||
|
} while (rhat < B);
|
||||||
|
|
||||||
|
uint64_t un21 = un32 * B + un1 - q1 * v; // Multiply and subtract
|
||||||
|
|
||||||
|
// Compute the second quotient digit, q0
|
||||||
|
uint64_t q0 = un21 / vn1;
|
||||||
|
rhat = un21 - q0 * vn1;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if ((q0 >= B) || ((q0 * vn0) > (B * rhat + un0)))
|
||||||
|
{
|
||||||
|
q0 = q0 - 1;
|
||||||
|
rhat = rhat + vn1;
|
||||||
|
}
|
||||||
|
else break;
|
||||||
|
} while (rhat < B);
|
||||||
|
|
||||||
|
// Calculate the remainder
|
||||||
|
// uint64_t r = (un21 * b + un0 - q0 * v) >> s;
|
||||||
|
// rem = (int64_t)r;
|
||||||
|
|
||||||
|
int64_t t = q1 * B + q0;
|
||||||
|
|
||||||
|
ifloat64 ret;
|
||||||
|
ret.s3132 = (sign_dif < 0) ? -(int64_t)t : (int64_t)t;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
static int64_t _safeMulti(const int64_t a, const int64_t b) {
|
||||||
|
int64_t sign_diff = a ^ b;
|
||||||
|
uint64_t abs_a = a < 0 ? -a : a;
|
||||||
|
uint64_t abs_b = b < 0 ? -b : b;
|
||||||
|
|
||||||
|
uint64_t ai = abs_a >> kShift;
|
||||||
|
uint64_t af = (abs_a & kFractionMask);
|
||||||
|
uint64_t bi = abs_b >> kShift;
|
||||||
|
uint64_t bf = (abs_b & kFractionMask);
|
||||||
|
|
||||||
|
// (Ai+Af)*(Bi+Bf)=Af*Bf + Ai*(Bi+Bf) + Af*Bi
|
||||||
|
int64_t t = ((af * bf) >> kShift) + ai * abs_b + af * bi;
|
||||||
|
return (sign_diff < 0) ? -t : t;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int32_t _nlz(uint64_t x)
|
||||||
|
{
|
||||||
|
int32_t n = 0;
|
||||||
|
if (x <= INT64_C(0x00000000FFFFFFFF)) { n = n + 32; x = x << 32; }
|
||||||
|
if (x <= INT64_C(0x0000FFFFFFFFFFFF)) { n = n + 16; x = x << 16; }
|
||||||
|
if (x <= INT64_C(0x00FFFFFFFFFFFFFF)) { n = n + 8; x = x << 8; }
|
||||||
|
if (x <= INT64_C(0x0FFFFFFFFFFFFFFF)) { n = n + 4; x = x << 4; }
|
||||||
|
if (x <= INT64_C(0x3FFFFFFFFFFFFFFF)) { n = n + 2; x = x << 2; }
|
||||||
|
if (x <= INT64_C(0x7FFFFFFFFFFFFFFF)) { n = n + 1; }
|
||||||
|
if (x == 0) return 64;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
int64_t s3132;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef ifloat32 iFloat32;
|
||||||
|
typedef ifloat64 iFloat64;
|
||||||
|
|
||||||
|
typedef ifloat64 iFloat;
|
||||||
|
|
||||||
|
struct iFloat2
|
||||||
|
{
|
||||||
|
iFloat x, y;
|
||||||
|
|
||||||
|
iFloat2() : x(0), y(0) {}
|
||||||
|
iFloat2(const iFloat2& other) : x(other.x), y(other.y) {}
|
||||||
|
iFloat2(float32_t _x, float32_t _y) : x(_x), y(_y) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct iFloat3
|
||||||
|
{
|
||||||
|
iFloat x, y, z;
|
||||||
|
|
||||||
|
inline void add(const iFloat3& other) {
|
||||||
|
x += other.x;
|
||||||
|
y += other.y;
|
||||||
|
z += other.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void sub(const iFloat3& other) {
|
||||||
|
x -= other.x;
|
||||||
|
y -= other.y;
|
||||||
|
z -= other.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void mul(const iFloat& multiplier) {
|
||||||
|
x = x * multiplier;
|
||||||
|
y = y * multiplier;
|
||||||
|
z = z * multiplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline iFloat operator [] (const size_t i) const {
|
||||||
|
return *(&x + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline iFloat& operator [] (const size_t i) {
|
||||||
|
return *(&x + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
iFloat3() : x(0), y(0), z(0) {}
|
||||||
|
iFloat3(const iFloat3& other) : x(other.x), y(other.y), z(other.z) {}
|
||||||
|
iFloat3(float32_t _x, float32_t _y, float32_t _z) : x(_x), y(_y), z(_z) {}
|
||||||
|
iFloat3(iFloat _x, iFloat _y, iFloat _z) : x(_x), y(_y), z(_z) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
DV_CORE_END_NAMESPACE
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "dvc_config.h"
|
|
||||||
|
|
||||||
DV_CORE_BEGIN_NAMESPACE
|
|
||||||
|
|
||||||
typedef int64_t ifloat_t;
|
|
||||||
//typedef int64_t ifloat2_t[2];
|
|
||||||
//typedef int64_t ifloat3_t[3];
|
|
||||||
|
|
||||||
#define IFLOAT_SHIFT (16)
|
|
||||||
#define IFLOAT_SCALE ((float64_t)(1<<IFLOAT_SHIFT))
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------------------
|
|
||||||
inline ifloat_t ifloat_init(float32_t a)
|
|
||||||
{
|
|
||||||
return (ifloat_t)((float64_t)a * IFLOAT_SCALE + 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------------------
|
|
||||||
inline ifloat_t ifloat_init(int32_t a)
|
|
||||||
{
|
|
||||||
return (int64_t)(a)<< IFLOAT_SHIFT;
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------------------
|
|
||||||
inline float32_t ifloat_get_float(ifloat_t a)
|
|
||||||
{
|
|
||||||
return (float32_t)((float64_t)a/ IFLOAT_SCALE);
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------------------
|
|
||||||
inline ifloat_t ifloat_multiply(ifloat_t a, ifloat_t b)
|
|
||||||
{
|
|
||||||
return (ifloat_t)((a * b) >> IFLOAT_SHIFT);
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------------------
|
|
||||||
//inline void ifloat3_add(ifloat3_t& a, const ifloat3_t& b)
|
|
||||||
//{
|
|
||||||
// a[0] += b[0]; a[1] += b[1]; a[2] += b[2];
|
|
||||||
//}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------------------
|
|
||||||
//inline void ifloat3_sub(ifloat3_t& a, const ifloat3_t& b)
|
|
||||||
//{
|
|
||||||
// a[0] -= b[0]; a[1] -= b[1]; a[2] -= b[2];
|
|
||||||
//}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------------------
|
|
||||||
//inline void ifloat3_cpy(ifloat3_t& a, const ifloat3_t& b)
|
|
||||||
//{
|
|
||||||
// a[0] = b[0]; a[1] = b[1]; a[2] = b[2];
|
|
||||||
//}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------------------
|
|
||||||
struct ifloat2_t
|
|
||||||
{
|
|
||||||
ifloat_t x, y;
|
|
||||||
|
|
||||||
ifloat2_t() : x(0ll), y(0ll) {}
|
|
||||||
ifloat2_t(const ifloat2_t& other) : x(other.x), y(other.y) {}
|
|
||||||
ifloat2_t(float32_t _x, float32_t _y) : x(ifloat_init(_x)), y(ifloat_init(_y)) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ifloat3_t
|
|
||||||
{
|
|
||||||
ifloat_t x, y, z;
|
|
||||||
|
|
||||||
inline void add(const ifloat3_t& other) {
|
|
||||||
x += other.x;
|
|
||||||
y += other.y;
|
|
||||||
z += other.z;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void sub(const ifloat3_t& other) {
|
|
||||||
x -= other.x;
|
|
||||||
y -= other.y;
|
|
||||||
z -= other.z;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline ifloat_t operator [] (const size_t i) const {
|
|
||||||
return *(&x + i);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline ifloat_t& operator [] (const size_t i) {
|
|
||||||
return *(&x + i);
|
|
||||||
}
|
|
||||||
|
|
||||||
ifloat3_t() : x(0ll), y(0ll), z(0ll) {}
|
|
||||||
ifloat3_t(const ifloat3_t& other) : x(other.x), y(other.y), z(other.z) {}
|
|
||||||
ifloat3_t(float32_t _x, float32_t _y, float32_t _z) : x(ifloat_init(_x)), y(ifloat_init(_y)), z(ifloat_init(_z)) {}
|
|
||||||
ifloat3_t(ifloat_t _x, ifloat_t _y, ifloat_t _z) : x(_x), y(_y), z(_z) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
DV_CORE_END_NAMESPACE
|
|
||||||
|
|
||||||
@@ -21,10 +21,24 @@ public:
|
|||||||
public:
|
public:
|
||||||
typedef std::function<void(const std::pair<int32_t, int32_t>& pos, const fVector3& percent)> DrawTriangleCallback;
|
typedef std::function<void(const std::pair<int32_t, int32_t>& pos, const fVector3& percent)> DrawTriangleCallback;
|
||||||
|
|
||||||
|
struct DebugParam
|
||||||
|
{
|
||||||
|
bool debug;
|
||||||
|
const char* szOutputFileName;
|
||||||
|
int32_t x;
|
||||||
|
int32_t y;
|
||||||
|
|
||||||
|
FILE* fpOutput;
|
||||||
|
int32_t tile_id;
|
||||||
|
int32_t coarse_id;
|
||||||
|
int32_t fine_id;
|
||||||
|
int32_t edge_id;
|
||||||
|
};
|
||||||
|
|
||||||
//Larrabee algorithm
|
//Larrabee algorithm
|
||||||
static void drawTriangleLarrabee(int32_t canvasWidth, int32_t canvasHeight,
|
static void drawTriangleLarrabee(int32_t canvasWidth, int32_t canvasHeight,
|
||||||
const fVector2& v0, const fVector2& v1, const fVector2& v2,
|
const fVector2& v0, const fVector2& v1, const fVector2& v2,
|
||||||
DrawTriangleCallback callback, bool ccw=true);
|
DrawTriangleCallback callback, bool ccw=true, const DebugParam* debugParam=nullptr);
|
||||||
|
|
||||||
//Scaleline algorithm
|
//Scaleline algorithm
|
||||||
static void drawTriangleScanline(int32_t canvasWidth, int32_t canvasHeight,
|
static void drawTriangleScanline(int32_t canvasWidth, int32_t canvasHeight,
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#include "math/dvc_fix_point.h"
|
||||||
|
|
||||||
|
DV_CORE_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
const ifloat64 ifloat64::kMax{ ifloat64::kMax_s3132 };
|
||||||
|
const ifloat64 ifloat64::kMin{ ifloat64::kMin_s3132 };
|
||||||
|
const ifloat64 ifloat64::kZero{ 0 };
|
||||||
|
const ifloat64 ifloat64::kOne{ INT64_C(1) << ifloat64::kShift };
|
||||||
|
const ifloat64 ifloat64::kTwo{ INT64_C(2) << kShift };
|
||||||
|
const ifloat64 ifloat64::kThree{ INT64_C(3) << ifloat64::kShift };
|
||||||
|
const ifloat64 ifloat64::kFour{ INT64_C(4) << ifloat64::kShift };
|
||||||
|
const ifloat64 ifloat64::kHalf{ INT64_C(1) << (ifloat64::kShift-1) };
|
||||||
|
const ifloat64 ifloat64::kPi{ INT64_C(0x3243f6a89) }; //static_cast<int64_t>(std::round(M_PI * kFloatScale));
|
||||||
|
const ifloat64 ifloat64::kPi2{ INT64_C(0x6487ed511) };
|
||||||
|
const ifloat64 ifloat64::kPiHalf{ INT64_C(0x1921fb544) };
|
||||||
|
const ifloat64 ifloat64::kE{ INT64_C(0x2b7e15163) };
|
||||||
|
const ifloat64 ifloat64::kNegOne{ INT64_C(-1) << kShift };
|
||||||
|
|
||||||
|
DV_CORE_END_NAMESPACE
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
#include "pipe/dvc_rasterizer.h"
|
#include "pipe/dvc_rasterizer.h"
|
||||||
#include "math/dvc_fixmath.h"
|
#include "math/dvc_fix_point.h"
|
||||||
#include "math/dvc_math_util.h"
|
#include "math/dvc_math_util.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -21,7 +21,8 @@ struct DrawTriangleParam
|
|||||||
float32_t bbox_max_y;
|
float32_t bbox_max_y;
|
||||||
bool tlBorder[3];
|
bool tlBorder[3];
|
||||||
float32_t area;
|
float32_t area;
|
||||||
ifloat3_t edgesDX, edgesDY;
|
iFloat3 edgesDX, edgesDY;
|
||||||
|
Rasterizer::DebugParam debugParam;
|
||||||
};
|
};
|
||||||
|
|
||||||
//-------------------------------------------------------------------------------------
|
//-------------------------------------------------------------------------------------
|
||||||
@@ -29,10 +30,15 @@ enum { TILE_WIDTH_IN_PIXELS = 64, COARSE_BLOCK_WIDTH_IN_PIXELS = 16, FINE_BLOCK_
|
|||||||
|
|
||||||
//-------------------------------------------------------------------------------------
|
//-------------------------------------------------------------------------------------
|
||||||
void _drawTriangle_Fine(int32_t tile_id, int32_t coarse_id, int32_t fine_id,
|
void _drawTriangle_Fine(int32_t tile_id, int32_t coarse_id, int32_t fine_id,
|
||||||
const DrawTriangleParam& param, const ifloat3_t& edges0, uint32_t testEdgeMask,
|
const DrawTriangleParam& param, const iFloat3& edges0, uint32_t testEdgeMask,
|
||||||
Rasterizer::DrawTriangleCallback callback)
|
Rasterizer::DrawTriangleCallback callback)
|
||||||
{
|
{
|
||||||
ifloat3_t pixelEdges(edges0);
|
iFloat3 kHalfDX = param.edgesDX; kHalfDX.mul(iFloat::kHalf);
|
||||||
|
iFloat3 kHalfDY = param.edgesDY; kHalfDY.mul(iFloat::kHalf);
|
||||||
|
|
||||||
|
iFloat3 pixelEdges(edges0);
|
||||||
|
pixelEdges.sub(kHalfDY);
|
||||||
|
pixelEdges.add(kHalfDX);
|
||||||
|
|
||||||
const fVector2 v0(param.verts[0]), v1(param.verts[1]), v2(param.verts[2]);
|
const fVector2 v0(param.verts[0]), v1(param.verts[1]), v2(param.verts[2]);
|
||||||
const bool edgeMask[3] = { (testEdgeMask & 1) != 0 , (testEdgeMask & 2) != 0 , (testEdgeMask & 4) != 0 };
|
const bool edgeMask[3] = { (testEdgeMask & 1) != 0 , (testEdgeMask & 2) != 0 , (testEdgeMask & 4) != 0 };
|
||||||
@@ -45,10 +51,39 @@ void _drawTriangle_Fine(int32_t tile_id, int32_t coarse_id, int32_t fine_id,
|
|||||||
|
|
||||||
for (int32_t y_index=0, y= fine_start_y; y_index < 4; y_index++, y++)
|
for (int32_t y_index=0, y= fine_start_y; y_index < 4; y_index++, y++)
|
||||||
{
|
{
|
||||||
ifloat3_t edgesRow(pixelEdges);
|
iFloat3 edgesRow(pixelEdges);
|
||||||
|
|
||||||
for (int32_t x_index = 0, x=fine_start_x; x_index < 4; x_index++, x++)
|
for (int32_t x_index = 0, x=fine_start_x; x_index < 4; x_index++, x++)
|
||||||
{
|
{
|
||||||
|
if (param.debugParam.debug)
|
||||||
|
{
|
||||||
|
if (x == param.debugParam.x && y == param.debugParam.y)
|
||||||
|
{
|
||||||
|
iFloat3 LBEdges(edges0);
|
||||||
|
LBEdges.sub(kHalfDY);
|
||||||
|
LBEdges.add(kHalfDX);
|
||||||
|
|
||||||
|
fprintf(param.debugParam.fpOutput, "======= Fine =======\n");
|
||||||
|
fprintf(param.debugParam.fpOutput, "LeftBottom=[%d,%d](%f,%f)\n", fine_start_x, fine_start_y, fine_start_x+0.5f, fine_start_y+0.5f);
|
||||||
|
fprintf(param.debugParam.fpOutput, "Edge0=(%f,%f,%f)[%lld,%lld,%lld]\n",
|
||||||
|
edges0.x.to_float(), edges0.y.to_float(), edges0.z.to_float(),
|
||||||
|
edges0.x.s3132, edges0.y.s3132, edges0.z.s3132
|
||||||
|
);
|
||||||
|
fprintf(param.debugParam.fpOutput, "Edge0+0.5=(%f,%f,%f)[%lld,%lld,%lld]\n",
|
||||||
|
LBEdges.x.to_float(), LBEdges.y.to_float(), LBEdges.z.to_float(),
|
||||||
|
LBEdges.x.s3132, LBEdges.y.s3132, LBEdges.z.s3132
|
||||||
|
);
|
||||||
|
|
||||||
|
fprintf(param.debugParam.fpOutput, "======= Pixel =======\n");
|
||||||
|
fprintf(param.debugParam.fpOutput, "Position=[%d,%d](%f,%f)\n", x, y, x+0.5f, y+0.5f);
|
||||||
|
fprintf(param.debugParam.fpOutput, "IndexInFind=[%d,%d]\n", x_index, y_index);
|
||||||
|
fprintf(param.debugParam.fpOutput, "Edge=(%f,%f,%f)[%lld,%lld,%lld]\n",
|
||||||
|
edgesRow.x.to_float(), edgesRow.y.to_float(), edgesRow.z.to_float(),
|
||||||
|
edgesRow.x.s3132, edgesRow.y.s3132, edgesRow.z.s3132
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool rejected =
|
bool rejected =
|
||||||
(edgeMask[0] && (edgesRow.x < 0 || (!param.tlBorder[0] && edgesRow.x == 0))) ||
|
(edgeMask[0] && (edgesRow.x < 0 || (!param.tlBorder[0] && edgesRow.x == 0))) ||
|
||||||
(edgeMask[1] && (edgesRow.y < 0 || (!param.tlBorder[1] && edgesRow.y == 0))) ||
|
(edgeMask[1] && (edgesRow.y < 0 || (!param.tlBorder[1] && edgesRow.y == 0))) ||
|
||||||
@@ -74,24 +109,24 @@ void _drawTriangle_Fine(int32_t tile_id, int32_t coarse_id, int32_t fine_id,
|
|||||||
|
|
||||||
//-------------------------------------------------------------------------------------
|
//-------------------------------------------------------------------------------------
|
||||||
void _drawTriangle_Coarse(int32_t tile_id, int32_t coarse_id,
|
void _drawTriangle_Coarse(int32_t tile_id, int32_t coarse_id,
|
||||||
DrawTriangleParam& param, const ifloat3_t& edges0, uint32_t testEdgeMask,
|
DrawTriangleParam& param, const iFloat3& edges0, uint32_t testEdgeMask,
|
||||||
Rasterizer::DrawTriangleCallback callback)
|
Rasterizer::DrawTriangleCallback callback)
|
||||||
{
|
{
|
||||||
const ifloat3_t blockEdgesDX(
|
const iFloat3 blockEdgesDX(
|
||||||
param.edgesDX.x * FINE_BLOCK_WIDTH_IN_PIXELS,
|
param.edgesDX.x * FINE_BLOCK_WIDTH_IN_PIXELS,
|
||||||
param.edgesDX.y * FINE_BLOCK_WIDTH_IN_PIXELS,
|
param.edgesDX.y * FINE_BLOCK_WIDTH_IN_PIXELS,
|
||||||
param.edgesDX.z * FINE_BLOCK_WIDTH_IN_PIXELS
|
param.edgesDX.z * FINE_BLOCK_WIDTH_IN_PIXELS
|
||||||
);
|
);
|
||||||
const ifloat3_t blockEdgesDY(
|
const iFloat3 blockEdgesDY(
|
||||||
param.edgesDY.x * FINE_BLOCK_WIDTH_IN_PIXELS,
|
param.edgesDY.x * FINE_BLOCK_WIDTH_IN_PIXELS,
|
||||||
param.edgesDY.y * FINE_BLOCK_WIDTH_IN_PIXELS,
|
param.edgesDY.y * FINE_BLOCK_WIDTH_IN_PIXELS,
|
||||||
param.edgesDY.z * FINE_BLOCK_WIDTH_IN_PIXELS
|
param.edgesDY.z * FINE_BLOCK_WIDTH_IN_PIXELS
|
||||||
);
|
);
|
||||||
const bool edgeMask[3] = { (testEdgeMask & 1) != 0 , (testEdgeMask & 2) != 0 , (testEdgeMask & 4) != 0 };
|
const bool edgeMask[3] = { (testEdgeMask & 1) != 0 , (testEdgeMask & 2) != 0 , (testEdgeMask & 4) != 0 };
|
||||||
|
|
||||||
ifloat3_t blockReject(edges0);
|
iFloat3 blockReject(edges0);
|
||||||
ifloat3_t blockAccept(edges0);
|
iFloat3 blockAccept(edges0);
|
||||||
ifloat3_t blockEdges(edges0);
|
iFloat3 blockEdges(edges0);
|
||||||
|
|
||||||
for (size_t v = 0; v < 3; v++)
|
for (size_t v = 0; v < 3; v++)
|
||||||
{
|
{
|
||||||
@@ -117,8 +152,8 @@ void _drawTriangle_Coarse(int32_t tile_id, int32_t coarse_id,
|
|||||||
|
|
||||||
if (block_start_y + (y_index + 1)*FINE_BLOCK_WIDTH_IN_PIXELS >= param.bbox_min_y)
|
if (block_start_y + (y_index + 1)*FINE_BLOCK_WIDTH_IN_PIXELS >= param.bbox_min_y)
|
||||||
{
|
{
|
||||||
ifloat3_t edgesRow(blockEdges);
|
iFloat3 edgesRow(blockEdges);
|
||||||
ifloat3_t edgesRowReject, edgesRowAccept;
|
iFloat3 edgesRowReject, edgesRowAccept;
|
||||||
|
|
||||||
for (size_t v = 0; v < 3; v++)
|
for (size_t v = 0; v < 3; v++)
|
||||||
{
|
{
|
||||||
@@ -182,25 +217,25 @@ void _drawTriangle_Coarse(int32_t tile_id, int32_t coarse_id,
|
|||||||
|
|
||||||
//-------------------------------------------------------------------------------------
|
//-------------------------------------------------------------------------------------
|
||||||
void _drawTriangle_Title(int32_t tile_id,
|
void _drawTriangle_Title(int32_t tile_id,
|
||||||
DrawTriangleParam& param, const ifloat3_t& edges0, uint32_t testEdgeMask,
|
DrawTriangleParam& param, const iFloat3& edges0, uint32_t testEdgeMask,
|
||||||
Rasterizer::DrawTriangleCallback callback)
|
Rasterizer::DrawTriangleCallback callback)
|
||||||
{
|
{
|
||||||
const ifloat3_t blockEdgesDX(
|
const iFloat3 blockEdgesDX(
|
||||||
param.edgesDX[0] * COARSE_BLOCK_WIDTH_IN_PIXELS,
|
param.edgesDX[0] * COARSE_BLOCK_WIDTH_IN_PIXELS,
|
||||||
param.edgesDX[1] * COARSE_BLOCK_WIDTH_IN_PIXELS,
|
param.edgesDX[1] * COARSE_BLOCK_WIDTH_IN_PIXELS,
|
||||||
param.edgesDX[2] * COARSE_BLOCK_WIDTH_IN_PIXELS
|
param.edgesDX[2] * COARSE_BLOCK_WIDTH_IN_PIXELS
|
||||||
);
|
);
|
||||||
|
|
||||||
const ifloat3_t blockEdgesDY(
|
const iFloat3 blockEdgesDY(
|
||||||
param.edgesDY[0] * COARSE_BLOCK_WIDTH_IN_PIXELS,
|
param.edgesDY[0] * COARSE_BLOCK_WIDTH_IN_PIXELS,
|
||||||
param.edgesDY[1] * COARSE_BLOCK_WIDTH_IN_PIXELS,
|
param.edgesDY[1] * COARSE_BLOCK_WIDTH_IN_PIXELS,
|
||||||
param.edgesDY[2] * COARSE_BLOCK_WIDTH_IN_PIXELS
|
param.edgesDY[2] * COARSE_BLOCK_WIDTH_IN_PIXELS
|
||||||
);
|
);
|
||||||
const bool edgeMask[3] = { (testEdgeMask & 1) != 0 , (testEdgeMask & 2) != 0 , (testEdgeMask & 4) != 0 };
|
const bool edgeMask[3] = { (testEdgeMask & 1) != 0 , (testEdgeMask & 2) != 0 , (testEdgeMask & 4) != 0 };
|
||||||
|
|
||||||
ifloat3_t blockReject(edges0);
|
iFloat3 blockReject(edges0);
|
||||||
ifloat3_t blockAccept(edges0);
|
iFloat3 blockAccept(edges0);
|
||||||
ifloat3_t blockEdges(edges0);
|
iFloat3 blockEdges(edges0);
|
||||||
|
|
||||||
for (size_t v = 0; v < 3; v++)
|
for (size_t v = 0; v < 3; v++)
|
||||||
{
|
{
|
||||||
@@ -226,8 +261,8 @@ void _drawTriangle_Title(int32_t tile_id,
|
|||||||
|
|
||||||
if (block_start_y + (y_index + 1)*COARSE_BLOCK_WIDTH_IN_PIXELS >= param.bbox_min_y)
|
if (block_start_y + (y_index + 1)*COARSE_BLOCK_WIDTH_IN_PIXELS >= param.bbox_min_y)
|
||||||
{
|
{
|
||||||
ifloat3_t edges(blockEdges);
|
iFloat3 edges(blockEdges);
|
||||||
ifloat3_t edgesRowReject, edgesRowAccept;
|
iFloat3 edgesRowReject, edgesRowAccept;
|
||||||
|
|
||||||
for (size_t v = 0; v < 3; v++)
|
for (size_t v = 0; v < 3; v++)
|
||||||
{
|
{
|
||||||
@@ -298,7 +333,7 @@ void _drawTriangle_Title(int32_t tile_id,
|
|||||||
//-------------------------------------------------------------------------------------
|
//-------------------------------------------------------------------------------------
|
||||||
void Rasterizer::drawTriangleLarrabee(int32_t canvasWidth, int32_t canvasHeight,
|
void Rasterizer::drawTriangleLarrabee(int32_t canvasWidth, int32_t canvasHeight,
|
||||||
const fVector2& v0, const fVector2& v1, const fVector2& v2,
|
const fVector2& v0, const fVector2& v1, const fVector2& v2,
|
||||||
DrawTriangleCallback callback, bool ccw)
|
DrawTriangleCallback callback, bool ccw, const DebugParam* debugParam)
|
||||||
{
|
{
|
||||||
assert(canvasWidth >= TILE_WIDTH_IN_PIXELS && canvasHeight >= TILE_WIDTH_IN_PIXELS);
|
assert(canvasWidth >= TILE_WIDTH_IN_PIXELS && canvasHeight >= TILE_WIDTH_IN_PIXELS);
|
||||||
assert(canvasWidth%TILE_WIDTH_IN_PIXELS == 0 && canvasHeight%TILE_WIDTH_IN_PIXELS == 0);
|
assert(canvasWidth%TILE_WIDTH_IN_PIXELS == 0 && canvasHeight%TILE_WIDTH_IN_PIXELS == 0);
|
||||||
@@ -306,20 +341,23 @@ void Rasterizer::drawTriangleLarrabee(int32_t canvasWidth, int32_t canvasHeight,
|
|||||||
DrawTriangleParam param;
|
DrawTriangleParam param;
|
||||||
param.widthInTiles = canvasWidth / TILE_WIDTH_IN_PIXELS;
|
param.widthInTiles = canvasWidth / TILE_WIDTH_IN_PIXELS;
|
||||||
|
|
||||||
//ccw
|
//Whether the three input vertices are in the agreed clockwise order or not,
|
||||||
|
// it indicates that the triangle is invisible
|
||||||
bool isCCW = (v1 - v0).crossProduct(v2 - v1)>0;
|
bool isCCW = (v1 - v0).crossProduct(v2 - v1)>0;
|
||||||
if (isCCW != ccw) return;
|
if (isCCW != ccw) return;
|
||||||
|
|
||||||
|
//By default, the order is counterclockwise. So if the input data is clockwise, a reversal is needed.
|
||||||
|
// Swap v1 and v2, and then reverse it back when the final callback is made
|
||||||
param.ccw = ccw;
|
param.ccw = ccw;
|
||||||
param.verts[0] = v0;
|
param.verts[0] = v0;
|
||||||
param.verts[1] = ccw ? v1 : v2;
|
param.verts[1] = ccw ? v1 : v2;
|
||||||
param.verts[2] = ccw ? v2 : v1;
|
param.verts[2] = ccw ? v2 : v1;
|
||||||
param.area = (param.verts[1] - param.verts[0]).crossProduct(param.verts[2] - param.verts[1]);
|
param.area = (param.verts[1] - param.verts[0]).crossProduct(param.verts[2] - param.verts[1]);
|
||||||
|
|
||||||
ifloat2_t vertices[3];
|
iFloat2 vertices[3];
|
||||||
for (size_t i = 0; i < 3; i++)
|
for (size_t i = 0; i < 3; i++)
|
||||||
{
|
{
|
||||||
vertices[i] = ifloat2_t(param.verts[i].x, param.verts[i].y);
|
vertices[i] = iFloat2(param.verts[i].x, param.verts[i].y);
|
||||||
}
|
}
|
||||||
|
|
||||||
//get window coordinates bounding box
|
//get window coordinates bounding box
|
||||||
@@ -329,8 +367,8 @@ void Rasterizer::drawTriangleLarrabee(int32_t canvasWidth, int32_t canvasHeight,
|
|||||||
param.bbox_max_y = MathUtil::max3(v0.y, v1.y, v2.y);
|
param.bbox_max_y = MathUtil::max3(v0.y, v1.y, v2.y);
|
||||||
|
|
||||||
//canvas size
|
//canvas size
|
||||||
ifloat_t iCanvasWidth = ifloat_init(canvasWidth);
|
iFloat iCanvasWidth(canvasWidth);
|
||||||
ifloat_t iCanvasHeight = ifloat_init(canvasHeight);
|
iFloat iCanvasHeight(canvasHeight);
|
||||||
|
|
||||||
// clip triangles that are fully outside the scissor rect (scissor rect = whole window)
|
// clip triangles that are fully outside the scissor rect (scissor rect = whole window)
|
||||||
if (param.bbox_max_x < 0 || param.bbox_max_y < 0 || param.bbox_min_x >= canvasWidth || param.bbox_min_y >= canvasHeight)
|
if (param.bbox_max_x < 0 || param.bbox_max_y < 0 || param.bbox_min_x >= canvasWidth || param.bbox_min_y >= canvasHeight)
|
||||||
@@ -348,13 +386,13 @@ void Rasterizer::drawTriangleLarrabee(int32_t canvasWidth, int32_t canvasHeight,
|
|||||||
int32_t last_tile_x = (int32_t)(param.bbox_max_x / TILE_WIDTH_IN_PIXELS);
|
int32_t last_tile_x = (int32_t)(param.bbox_max_x / TILE_WIDTH_IN_PIXELS);
|
||||||
int32_t last_tile_y = (int32_t)(param.bbox_max_y / TILE_WIDTH_IN_PIXELS);
|
int32_t last_tile_y = (int32_t)(param.bbox_max_y / TILE_WIDTH_IN_PIXELS);
|
||||||
|
|
||||||
// evaluate edge equation at the top left tile
|
//The first Title is the Title at the lower left corner within the bounding box range covered by the triangle
|
||||||
ifloat_t firstTileX = first_tile_x * ifloat_init(TILE_WIDTH_IN_PIXELS);
|
iFloat firstTileX = iFloat(first_tile_x * TILE_WIDTH_IN_PIXELS);
|
||||||
ifloat_t firstTileY = first_tile_y * ifloat_init(TILE_WIDTH_IN_PIXELS);
|
iFloat firstTileY = iFloat(first_tile_y * TILE_WIDTH_IN_PIXELS);
|
||||||
|
|
||||||
ifloat3_t edges0;
|
iFloat3 edgesLB; //The edge value at the lower left corner of the first title
|
||||||
ifloat3_t tileEdgesDX, tileEdgesDY, edgesReject, edgesAccept;
|
iFloat3 tileEdgesDX, tileEdgesDY;
|
||||||
const ifloat_t kZeroPointFive = ifloat_init(0.5f);
|
iFloat3 edgesReject, edgesAccept;
|
||||||
|
|
||||||
for (size_t v = 0; v < 3; v++)
|
for (size_t v = 0; v < 3; v++)
|
||||||
{
|
{
|
||||||
@@ -366,8 +404,8 @@ void Rasterizer::drawTriangleLarrabee(int32_t canvasWidth, int32_t canvasHeight,
|
|||||||
tileEdgesDY[v] = param.edgesDY[v] * TILE_WIDTH_IN_PIXELS;
|
tileEdgesDY[v] = param.edgesDY[v] * TILE_WIDTH_IN_PIXELS;
|
||||||
|
|
||||||
//(V0,V1) X (V0, P) = (x1-x0)(py-y0)-(y1-y0)(px-x0)
|
//(V0,V1) X (V0, P) = (x1-x0)(py-y0)-(y1-y0)(px-x0)
|
||||||
edges0[v] = ifloat_multiply(param.edgesDX[v], (firstTileY + kZeroPointFive - vertices[v].y))
|
edgesLB[v] = param.edgesDX[v] * (firstTileY - vertices[v].y)
|
||||||
- ifloat_multiply(param.edgesDY[v], (firstTileX + kZeroPointFive - vertices[v].x));
|
- param.edgesDY[v] * (firstTileX - vertices[v].x);
|
||||||
|
|
||||||
// Top-left rule:
|
// Top-left rule:
|
||||||
// shift top-left edges ever so slightly outward to make the top-left edges be
|
// shift top-left edges ever so slightly outward to make the top-left edges be
|
||||||
@@ -382,7 +420,7 @@ void Rasterizer::drawTriangleLarrabee(int32_t canvasWidth, int32_t canvasHeight,
|
|||||||
param.tlBorder[v] = false;
|
param.tlBorder[v] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
edgesReject[v] = edgesAccept[v] = edges0[v];
|
edgesReject[v] = edgesAccept[v] = edgesLB[v];
|
||||||
|
|
||||||
if (tileEdgesDX[v] > 0) edgesReject[v] += tileEdgesDX[v];
|
if (tileEdgesDX[v] > 0) edgesReject[v] += tileEdgesDX[v];
|
||||||
else if (tileEdgesDX[v] < 0) edgesAccept[v] += tileEdgesDX[v];
|
else if (tileEdgesDX[v] < 0) edgesAccept[v] += tileEdgesDX[v];
|
||||||
@@ -391,18 +429,77 @@ void Rasterizer::drawTriangleLarrabee(int32_t canvasWidth, int32_t canvasHeight,
|
|||||||
else if (tileEdgesDY[v] < 0) edgesReject[v] -= tileEdgesDY[v];
|
else if (tileEdgesDY[v] < 0) edgesReject[v] -= tileEdgesDY[v];
|
||||||
}
|
}
|
||||||
|
|
||||||
ifloat3_t rowEdges(edges0);
|
if (debugParam != nullptr)
|
||||||
|
{
|
||||||
|
param.debugParam.debug = debugParam->debug;
|
||||||
|
param.debugParam.szOutputFileName = debugParam->szOutputFileName;
|
||||||
|
param.debugParam.x = debugParam->x;
|
||||||
|
param.debugParam.y = debugParam->y;
|
||||||
|
|
||||||
|
if (param.debugParam.debug) {
|
||||||
|
param.debugParam.fpOutput = fopen(debugParam->szOutputFileName, "w");
|
||||||
|
|
||||||
|
fprintf(param.debugParam.fpOutput, "===== Triangle ====\n");
|
||||||
|
for (int32_t i = 0; i < 3; i++) {
|
||||||
|
fprintf(param.debugParam.fpOutput, "v%d=(%f,%f)[%lld,%lld]\n", i,
|
||||||
|
param.verts[i].x, param.verts[i].y, vertices[i].x.s3132, vertices[i].y.s3132);
|
||||||
|
}
|
||||||
|
fprintf(param.debugParam.fpOutput, "===== TitleRange ====\n");
|
||||||
|
fprintf(param.debugParam.fpOutput,
|
||||||
|
"tile_x={%d, %d}\ntile_y={%d, %d}\n",
|
||||||
|
first_tile_x, last_tile_x, first_tile_y, last_tile_y);
|
||||||
|
fprintf(param.debugParam.fpOutput,
|
||||||
|
"FirstTitle=(%f,%f)[%lld,%lld]\n", firstTileX.to_float(), firstTileY.to_float(),
|
||||||
|
firstTileX.s3132, firstTileY.s3132
|
||||||
|
);
|
||||||
|
|
||||||
|
fprintf(param.debugParam.fpOutput, "===== Edge ====\n");
|
||||||
|
for (int32_t i = 0; i < 3; i++) {
|
||||||
|
fprintf(param.debugParam.fpOutput, "DX%d=(%f)[%lld]\tDY%d=(%f)[%lld]\n",
|
||||||
|
i,
|
||||||
|
param.edgesDX[i].to_float(), param.edgesDX[i].s3132,
|
||||||
|
i,
|
||||||
|
param.edgesDY[i].to_float(), param.edgesDY[i].s3132);
|
||||||
|
}
|
||||||
|
fprintf(param.debugParam.fpOutput, "\n");
|
||||||
|
for (int32_t i = 0; i < 3; i++) {
|
||||||
|
fprintf(param.debugParam.fpOutput, "Edge%d=(%lld)*(y-%lld)-(%lld)*(x-%lld)\n\t=(%f)*(y-%f)-(%f)*(x-%f)\n", i,
|
||||||
|
param.edgesDX[i].s3132, vertices[i].y.s3132, param.edgesDY[i].s3132, vertices[i].x.s3132,
|
||||||
|
param.edgesDX[i].to_float(), vertices[i].y.to_float(), param.edgesDY[i].to_float(), vertices[i].x.to_float()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
fprintf(param.debugParam.fpOutput, "EdgeLB=(%f,%f,%f)[%lld,%lld,%lld]\n",
|
||||||
|
edgesLB.x.to_float(), edgesLB.y.to_float(), edgesLB.z.to_float(),
|
||||||
|
edgesLB.x.s3132, edgesLB.y.s3132, edgesLB.z.s3132
|
||||||
|
);
|
||||||
|
for (int32_t i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
fprintf(param.debugParam.fpOutput, "EdgeLB.%d=%lld*(%lld-%lld)-%lld*(%lld-%lld)\n", i,
|
||||||
|
param.edgesDX[i].s3132, firstTileY.s3132, vertices[i].y.s3132,
|
||||||
|
param.edgesDY[i].s3132, firstTileX.s3132, vertices[i].x.s3132
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(param.debugParam.fpOutput, "TopLeft=%s,%s,%s\n",
|
||||||
|
param.tlBorder[0] ? "true" : "false", param.tlBorder[1] ? "true" : "false", param.tlBorder[2] ? "true" : "false"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
iFloat3 rowEdges(edgesLB);
|
||||||
|
|
||||||
int32_t tile_row_start = first_tile_y * param.widthInTiles + first_tile_x;
|
int32_t tile_row_start = first_tile_y * param.widthInTiles + first_tile_x;
|
||||||
for (int32_t tile_y = first_tile_y; tile_y <= last_tile_y; tile_y++)
|
for (int32_t tile_y = first_tile_y; tile_y <= last_tile_y; tile_y++)
|
||||||
{
|
{
|
||||||
ifloat3_t edges(rowEdges);
|
iFloat3 edges(rowEdges);
|
||||||
ifloat3_t tileEdgesReject(edgesReject);
|
iFloat3 tileEdgesReject(edgesReject);
|
||||||
ifloat3_t tileEdgesAccept(edgesAccept);
|
iFloat3 tileEdgesAccept(edgesAccept);
|
||||||
|
|
||||||
int32_t tile_i = tile_row_start;
|
int32_t tile_i = tile_row_start;
|
||||||
for (int32_t tile_x = first_tile_x; tile_x <= last_tile_x; tile_x++)
|
for (int32_t tile_x = first_tile_x; tile_x <= last_tile_x; tile_x++)
|
||||||
{
|
{
|
||||||
|
//If the title is excluded by any edge,
|
||||||
|
// it indicates that the title is completely outside the triangle and does not require rendering
|
||||||
bool rejected =
|
bool rejected =
|
||||||
((tileEdgesReject[0] < 0 || (!param.tlBorder[0] && tileEdgesReject[0] == 0))) ||
|
((tileEdgesReject[0] < 0 || (!param.tlBorder[0] && tileEdgesReject[0] == 0))) ||
|
||||||
((tileEdgesReject[1] < 0 || (!param.tlBorder[1] && tileEdgesReject[1] == 0))) ||
|
((tileEdgesReject[1] < 0 || (!param.tlBorder[1] && tileEdgesReject[1] == 0))) ||
|
||||||
@@ -416,7 +513,9 @@ void Rasterizer::drawTriangleLarrabee(int32_t canvasWidth, int32_t canvasHeight,
|
|||||||
{
|
{
|
||||||
if (tileEdgesAccept[v] < 0 || (tileEdgesAccept[v] == 0 && !param.tlBorder[v]))
|
if (tileEdgesAccept[v] < 0 || (tileEdgesAccept[v] == 0 && !param.tlBorder[v]))
|
||||||
{
|
{
|
||||||
testEdgeMask += (1 << v);
|
//whether the accepting vertex of this title is on the "outside" of the three edges,
|
||||||
|
// that is, it may intersect or be completely outside
|
||||||
|
testEdgeMask += (1 << v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -436,6 +535,12 @@ void Rasterizer::drawTriangleLarrabee(int32_t canvasWidth, int32_t canvasHeight,
|
|||||||
edgesAccept.add(tileEdgesDX);
|
edgesAccept.add(tileEdgesDX);
|
||||||
tile_row_start += param.widthInTiles;
|
tile_row_start += param.widthInTiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (debugParam != nullptr && debugParam->debug)
|
||||||
|
{
|
||||||
|
fclose(param.debugParam.fpOutput);
|
||||||
|
param.debugParam.fpOutput = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------------------------------------------------------------------
|
//-------------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ set(DVT_UNIT_SOURCE_FILES
|
|||||||
dvt_unit_vector4.cpp
|
dvt_unit_vector4.cpp
|
||||||
dvt_unit_matrix3.cpp
|
dvt_unit_matrix3.cpp
|
||||||
dvt_unit_matrix4.cpp
|
dvt_unit_matrix4.cpp
|
||||||
|
dvt_unit_fix_point.cpp
|
||||||
dvt_unit_math_util.cpp
|
dvt_unit_math_util.cpp
|
||||||
dvt_unit_rasterizer.cpp
|
dvt_unit_rasterizer.cpp
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ using namespace davinci;
|
|||||||
|
|
||||||
//-------------------------------------------------------------------------------------
|
//-------------------------------------------------------------------------------------
|
||||||
inline float _randomFloat(void) {
|
inline float _randomFloat(void) {
|
||||||
return MathUtil::rangeRandom(-65521 / 16.f, 65521 / 16.f);
|
return MathUtil::rangeRandom(-65521 / 24.f, 65521 / 24.f);
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------------------------------------------------------------------
|
//-------------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -0,0 +1,448 @@
|
|||||||
|
#include "math/dvc_fix_point.h"
|
||||||
|
#include "dvt_unit_common.h"
|
||||||
|
|
||||||
|
#define _USE_MATH_DEFINES
|
||||||
|
#include <cmath>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
using namespace davinci;
|
||||||
|
|
||||||
|
TEST(FixPoint32, Basic)
|
||||||
|
{
|
||||||
|
iFloat32 a;
|
||||||
|
EXPECT_EQ(a.s1516, 0);
|
||||||
|
|
||||||
|
a = iFloat32(0);
|
||||||
|
EXPECT_EQ(a.s1516, iFloat32::kZero_s1516);
|
||||||
|
EXPECT_EQ(a.to_float(), 0.f);
|
||||||
|
EXPECT_EQ(a.to_int32(), 0);
|
||||||
|
|
||||||
|
a = iFloat32(0.5f);
|
||||||
|
EXPECT_EQ(a.s1516, iFloat32::kHalf_s1516);
|
||||||
|
EXPECT_EQ(a.to_float(), 0.5f);
|
||||||
|
EXPECT_EQ(a.to_int32(), 0);
|
||||||
|
|
||||||
|
a = iFloat32(1.0f);
|
||||||
|
EXPECT_EQ(a.s1516, iFloat32::kOne_s1516);
|
||||||
|
EXPECT_EQ(a.to_float(), 1.f);
|
||||||
|
EXPECT_EQ(a.to_int32(), 1);
|
||||||
|
|
||||||
|
a = iFloat32(-0.5f);
|
||||||
|
EXPECT_EQ(a.s1516, -iFloat32::kHalf_s1516);
|
||||||
|
EXPECT_EQ(a.to_float(), -0.5f);
|
||||||
|
EXPECT_EQ(a.to_int32(), -1);
|
||||||
|
|
||||||
|
a = iFloat32(-1.f);
|
||||||
|
EXPECT_EQ(a.s1516, iFloat32::kNegOne_s1516);
|
||||||
|
EXPECT_EQ(a.to_float(), -1.f);
|
||||||
|
EXPECT_EQ(a.to_int32(), -1);
|
||||||
|
|
||||||
|
a = iFloat32(1);
|
||||||
|
EXPECT_EQ(a.s1516, iFloat32::kOne_s1516);
|
||||||
|
EXPECT_EQ(a.to_float(), 1.f);
|
||||||
|
EXPECT_EQ(a.to_int32(), 1);
|
||||||
|
|
||||||
|
a = iFloat32(-1);
|
||||||
|
EXPECT_EQ(a.s1516, iFloat32::kNegOne_s1516);
|
||||||
|
EXPECT_EQ(a.to_float(), -1.f);
|
||||||
|
EXPECT_EQ(a.to_int32(), -1);
|
||||||
|
|
||||||
|
a = iFloat32(M_PI); //3.14
|
||||||
|
EXPECT_EQ(a.s1516, (int32_t)(M_PI * iFloat32::kFloatScale));
|
||||||
|
EXPECT_EQ(a.to_float(), ((int32_t)(M_PI * iFloat32::kFloatScale)) / (iFloat32::kFloatScale));
|
||||||
|
EXPECT_EQ(a.to_int32(), 3);
|
||||||
|
|
||||||
|
a = iFloat32(-M_PI); //-3.14
|
||||||
|
EXPECT_EQ(a.s1516, -(int32_t)(M_PI * iFloat32::kFloatScale));
|
||||||
|
EXPECT_EQ(a.to_float(), -((int32_t)(M_PI * iFloat32::kFloatScale)) / (iFloat32::kFloatScale));
|
||||||
|
EXPECT_EQ(a.to_int32(), -4);
|
||||||
|
|
||||||
|
a = iFloat32(-M_E); //-2.718
|
||||||
|
EXPECT_EQ(a.s1516, -(int32_t)(M_E * iFloat32::kFloatScale));
|
||||||
|
EXPECT_EQ(a.to_float(), -((int32_t)(M_E * iFloat32::kFloatScale)) / (iFloat32::kFloatScale));
|
||||||
|
EXPECT_EQ(a.to_int32(), -3);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FixPoint32, Range)
|
||||||
|
{
|
||||||
|
iFloat32 a;
|
||||||
|
|
||||||
|
a = iFloat32(iFloat32::kMax_float);
|
||||||
|
EXPECT_EQ(a.s1516, iFloat32::kMax_s1516);
|
||||||
|
EXPECT_EQ(a.to_float(), iFloat32::kMax_float);
|
||||||
|
EXPECT_EQ(a.to_int32(), iFloat32::kMax_int32);
|
||||||
|
|
||||||
|
a = iFloat32(iFloat32::kMax_float + 0.1f);
|
||||||
|
EXPECT_LT(a.s1516, 0);
|
||||||
|
EXPECT_LT(a.to_float(), 0);
|
||||||
|
EXPECT_LT(a.to_int32(), 0);
|
||||||
|
|
||||||
|
//a = iFloat32(iFloat32::kMax_int32);
|
||||||
|
//EXPECT_LT(a.s1516, iFloat32::kMax_s1516);
|
||||||
|
//EXPECT_LT(a.to_float(), iFloat32::kMax_float);
|
||||||
|
//EXPECT_EQ(a.to_int32(), iFloat32::kMax_int32);
|
||||||
|
|
||||||
|
a = iFloat32(iFloat32::kMax_int32 + 1);
|
||||||
|
EXPECT_LT(a.s1516, 0);
|
||||||
|
EXPECT_LT(a.to_float(), 0);
|
||||||
|
EXPECT_LT(a.to_int32(), 0);
|
||||||
|
|
||||||
|
a = iFloat32(iFloat32::kMin_float);
|
||||||
|
EXPECT_EQ(a.s1516, iFloat32::kMin_s1516);
|
||||||
|
EXPECT_EQ(a.to_float(), iFloat32::kMin_float);
|
||||||
|
EXPECT_EQ(a.to_int32(), iFloat32::kMin_int32);
|
||||||
|
|
||||||
|
a = iFloat32(iFloat32::kMin_float-1.f);
|
||||||
|
EXPECT_EQ(a.s1516, iFloat32::kMin_s1516);
|
||||||
|
EXPECT_EQ(a.to_float(), iFloat32::kMin_float);
|
||||||
|
EXPECT_EQ(a.to_int32(), iFloat32::kMin_int32);
|
||||||
|
|
||||||
|
a = iFloat32(iFloat32::kMin_int32);
|
||||||
|
EXPECT_EQ(a.s1516, iFloat32::kMin_s1516);
|
||||||
|
EXPECT_EQ(a.to_float(), iFloat32::kMin_float);
|
||||||
|
EXPECT_EQ(a.to_int32(), iFloat32::kMin_int32);
|
||||||
|
|
||||||
|
a = iFloat32(iFloat32::kMin_int32 - 1);
|
||||||
|
EXPECT_GT(a.s1516, 0);
|
||||||
|
EXPECT_GT(a.to_float(), 0);
|
||||||
|
EXPECT_GT(a.to_int32(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FixPoint32, Compare)
|
||||||
|
{
|
||||||
|
float a = _randomFloat();
|
||||||
|
iFloat32 ia = a;
|
||||||
|
EXPECT_EQ(ia.to_float(), a);
|
||||||
|
|
||||||
|
int32_t n = MathUtil::rangeRandom(iFloat32::kMin_int32, iFloat32::kMax_int32);
|
||||||
|
iFloat32 in = n;
|
||||||
|
EXPECT_EQ(in.to_int32(), n);
|
||||||
|
|
||||||
|
float f1 = _randomFloat(), f2 = f1 + 1.f;
|
||||||
|
iFloat32 iF1 = f1, iF2 = f2;
|
||||||
|
|
||||||
|
EXPECT_LT(iF1, iF2);
|
||||||
|
EXPECT_GT(iF2, iF1);
|
||||||
|
EXPECT_NE(iF1, iF2);
|
||||||
|
EXPECT_EQ(iF1, iF1);
|
||||||
|
|
||||||
|
iF1 += iF2;
|
||||||
|
EXPECT_EQ(iF1, f1 + f2);
|
||||||
|
|
||||||
|
iF1 -= iF2;
|
||||||
|
EXPECT_EQ(iF1, f1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FixPoint32, Operation)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < 500; i++)
|
||||||
|
{
|
||||||
|
float a = _randomFloat();
|
||||||
|
float b = _randomFloat();
|
||||||
|
|
||||||
|
iFloat32 ia(a), ib(b);
|
||||||
|
iFloat32 ret = ia + ib;
|
||||||
|
|
||||||
|
EXPECT_EQ(ret.to_float(), a + b);
|
||||||
|
|
||||||
|
iFloat32 ic(a);
|
||||||
|
iFloat32 ret2 = ic + b;
|
||||||
|
EXPECT_EQ(ret2.to_float(), a + b);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < 500; i++)
|
||||||
|
{
|
||||||
|
float a = _randomFloat();
|
||||||
|
float b = _randomFloat();
|
||||||
|
|
||||||
|
iFloat32 ia(a), ib(b);
|
||||||
|
iFloat32 ret = ia - ib;
|
||||||
|
|
||||||
|
EXPECT_EQ(ret.to_float(), a - b);
|
||||||
|
|
||||||
|
iFloat32 ic(a);
|
||||||
|
iFloat32 ret2 = ic - b;
|
||||||
|
EXPECT_EQ(ret2.to_float(), a - b);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < 500; i++)
|
||||||
|
{
|
||||||
|
float32_t a, b;
|
||||||
|
int32_t c;
|
||||||
|
float32_t ret1, ret2;
|
||||||
|
do {
|
||||||
|
a = _randomFloat();
|
||||||
|
b = _randomFloat();
|
||||||
|
|
||||||
|
ret1 = a * b;
|
||||||
|
if (ret1<iFloat32::kMin_float || ret1>iFloat32::kMax_float) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
c = rand() & 0xFFF;
|
||||||
|
ret2 = a * c;
|
||||||
|
if (ret2<iFloat32::kMin_float || ret2>iFloat32::kMax_float) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} while (true);
|
||||||
|
|
||||||
|
iFloat32 ia(a), ib(b);
|
||||||
|
iFloat32 iret1 = ia * ib;
|
||||||
|
EXPECT_LE(std::abs(iret1.to_float() - ret1), 0.01f);
|
||||||
|
|
||||||
|
iFloat32 iret2 = ia * b;
|
||||||
|
EXPECT_EQ(iret2, iret1);
|
||||||
|
|
||||||
|
iFloat32 iret3 = ia * c;
|
||||||
|
EXPECT_EQ(iret3.to_float(), ret2);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < 500; i++)
|
||||||
|
{
|
||||||
|
float a, b, ret;
|
||||||
|
a = _randomFloat();
|
||||||
|
b = _randomFloat();
|
||||||
|
ret = a / b;
|
||||||
|
|
||||||
|
if (ret < iFloat32::kMin_float) ret = iFloat32::kMin_float;
|
||||||
|
else if (ret > iFloat32::kMax_float) ret = iFloat32::kMax_float;
|
||||||
|
|
||||||
|
iFloat32 ia(a), ib(b);
|
||||||
|
iFloat32 iret = ia / ib;
|
||||||
|
|
||||||
|
EXPECT_LE(std::abs(iret.to_float() - ret), 0.001f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FixPoint64, Basic)
|
||||||
|
{
|
||||||
|
iFloat64 a;
|
||||||
|
EXPECT_EQ(a.s3132, 0);
|
||||||
|
|
||||||
|
a = iFloat64(0);
|
||||||
|
EXPECT_EQ(a.s3132, iFloat64::kZero.s3132);
|
||||||
|
EXPECT_EQ(a.to_float(), 0.f);
|
||||||
|
EXPECT_EQ(a.to_int32(), 0);
|
||||||
|
|
||||||
|
a = iFloat64(0.5f);
|
||||||
|
EXPECT_EQ(a, iFloat64::kHalf);
|
||||||
|
EXPECT_EQ(a.s3132, iFloat64::kHalf.s3132);
|
||||||
|
EXPECT_EQ(a.to_float(), 0.5f);
|
||||||
|
EXPECT_EQ(a.to_int32(), 0);
|
||||||
|
|
||||||
|
a = iFloat64(1.0f);
|
||||||
|
EXPECT_EQ(a, iFloat64::kOne);
|
||||||
|
EXPECT_EQ(a.s3132, iFloat64::kOne.s3132);
|
||||||
|
EXPECT_EQ(a.to_float(), 1.f);
|
||||||
|
EXPECT_EQ(a.to_int32(), 1);
|
||||||
|
|
||||||
|
a = iFloat64(-0.5f);
|
||||||
|
EXPECT_EQ(a, -iFloat64::kHalf);
|
||||||
|
EXPECT_EQ(a.s3132, -iFloat64::kHalf.s3132);
|
||||||
|
EXPECT_EQ(a.to_float(), -0.5f);
|
||||||
|
EXPECT_EQ(a.to_int32(), -1);
|
||||||
|
|
||||||
|
a = iFloat64(-1.f);
|
||||||
|
EXPECT_EQ(a, -iFloat64::kOne);
|
||||||
|
EXPECT_EQ(a.s3132, iFloat64::kNegOne.s3132);
|
||||||
|
EXPECT_EQ(a.to_float(), -1.f);
|
||||||
|
EXPECT_EQ(a.to_int32(), -1);
|
||||||
|
|
||||||
|
a = iFloat64(1);
|
||||||
|
EXPECT_EQ(a, iFloat64::kOne);
|
||||||
|
EXPECT_EQ(a.s3132, iFloat64::kOne.s3132);
|
||||||
|
EXPECT_EQ(a.to_float(), 1.f);
|
||||||
|
EXPECT_EQ(a.to_int32(), 1);
|
||||||
|
|
||||||
|
a = iFloat64(-1);
|
||||||
|
EXPECT_EQ(a, -iFloat64::kOne);
|
||||||
|
EXPECT_EQ(a.s3132, iFloat64::kNegOne.s3132);
|
||||||
|
EXPECT_EQ(a.to_float(), -1.f);
|
||||||
|
EXPECT_EQ(a.to_int32(), -1);
|
||||||
|
|
||||||
|
a = iFloat64(M_PI); //3.14
|
||||||
|
EXPECT_EQ(a, iFloat64::kPi);
|
||||||
|
EXPECT_EQ(a.s3132, iFloat64::kPi.s3132);
|
||||||
|
EXPECT_EQ(a.to_float(), (float32_t)((int64_t)(M_PI * iFloat64::kFloatScale)) / (iFloat64::kFloatScale));
|
||||||
|
EXPECT_EQ(a.to_int32(), 3);
|
||||||
|
|
||||||
|
a = iFloat64(M_PI*2); //2*3.14
|
||||||
|
EXPECT_EQ(a, iFloat64::kPi2);
|
||||||
|
EXPECT_EQ(a.s3132, iFloat64::kPi2.s3132);
|
||||||
|
EXPECT_EQ(a.to_float(), (float32_t)((int64_t)(2 * M_PI * iFloat64::kFloatScale)) / (iFloat64::kFloatScale));
|
||||||
|
EXPECT_EQ(a.to_int32(), 6);
|
||||||
|
|
||||||
|
a = iFloat64(M_PI * 0.5); //0.5*3.14
|
||||||
|
EXPECT_EQ(a, iFloat64::kPiHalf);
|
||||||
|
EXPECT_EQ(a.s3132, iFloat64::kPiHalf.s3132);
|
||||||
|
EXPECT_EQ(a.to_float(), (float32_t)((int64_t)(0.5 * M_PI * iFloat64::kFloatScale)) / (iFloat64::kFloatScale));
|
||||||
|
EXPECT_EQ(a.to_int32(), 1);
|
||||||
|
|
||||||
|
a = iFloat64(-M_PI); //-3.14
|
||||||
|
EXPECT_EQ(a, -iFloat64::kPi);
|
||||||
|
EXPECT_EQ(a.s3132, -iFloat64::kPi.s3132);
|
||||||
|
EXPECT_EQ(a.to_float(), -(float32_t)((int64_t)(M_PI * iFloat64::kFloatScale)) / (iFloat64::kFloatScale));
|
||||||
|
EXPECT_EQ(a.to_int32(), -4);
|
||||||
|
|
||||||
|
a = iFloat64(M_E); //2.718
|
||||||
|
EXPECT_EQ(a, iFloat64::kE);
|
||||||
|
EXPECT_EQ(a.s3132, iFloat64::kE.s3132);
|
||||||
|
EXPECT_EQ(a.to_float(), (float32_t)((int64_t)(M_E * iFloat64::kFloatScale)) / (iFloat64::kFloatScale));
|
||||||
|
EXPECT_EQ(a.to_int32(), 2);
|
||||||
|
|
||||||
|
a = iFloat64(-M_E); //-2.718
|
||||||
|
EXPECT_EQ(a, -iFloat64::kE);
|
||||||
|
EXPECT_EQ(a.s3132, -iFloat64::kE.s3132);
|
||||||
|
EXPECT_EQ(a.to_float(), -(float32_t)((int64_t)(M_E * iFloat64::kFloatScale)) / (iFloat64::kFloatScale));
|
||||||
|
EXPECT_EQ(a.to_int32(), -3);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FixPoint64, Range)
|
||||||
|
{
|
||||||
|
iFloat64 a;
|
||||||
|
|
||||||
|
static constexpr int32_t kShift = 32;
|
||||||
|
static constexpr float64_t kFloatScale = static_cast<float64_t>(1LL << kShift); //4294967296.0
|
||||||
|
static constexpr float64_t kFloatInvScale = 1.0 / kFloatScale;
|
||||||
|
|
||||||
|
a = iFloat64(iFloat64::kMax_float);
|
||||||
|
EXPECT_LE(a.s3132, iFloat64::kMax_s3132);
|
||||||
|
EXPECT_LE(a.to_float(), iFloat64::kMax_float);
|
||||||
|
EXPECT_LE(a.to_int32(), iFloat64::kMax_int32);
|
||||||
|
|
||||||
|
a = iFloat64(nextafter(iFloat64::kMax_float, FLT_MAX));
|
||||||
|
EXPECT_LT(a.s3132, 0);
|
||||||
|
EXPECT_LT(a.to_float(), 0.f);
|
||||||
|
EXPECT_LT(a.to_int32(), 0);
|
||||||
|
|
||||||
|
a = iFloat64(iFloat64::kMax_int32);
|
||||||
|
EXPECT_LE(a.s3132, iFloat64::kMax_s3132);
|
||||||
|
EXPECT_LE(a.to_int32(), iFloat64::kMax_int32);
|
||||||
|
|
||||||
|
a = iFloat64(iFloat64::kMax_int32+1);
|
||||||
|
EXPECT_LT(a.s3132, 0);
|
||||||
|
EXPECT_LT(a.to_float(), 0.f);
|
||||||
|
EXPECT_LT(a.to_int32(), 0);
|
||||||
|
|
||||||
|
a = iFloat64(iFloat64::kMin_float);
|
||||||
|
EXPECT_EQ(a.s3132, iFloat64::kMin_s3132);
|
||||||
|
EXPECT_EQ(a.to_float(), iFloat64::kMin_float);
|
||||||
|
EXPECT_EQ(a.to_int32(), iFloat64::kMin_int32);
|
||||||
|
|
||||||
|
a = iFloat64(iFloat64::kMin_float - 1.f);
|
||||||
|
EXPECT_EQ(a.s3132, iFloat64::kMin_s3132);
|
||||||
|
EXPECT_EQ(a.to_float(), iFloat64::kMin_float);
|
||||||
|
EXPECT_EQ(a.to_int32(), iFloat64::kMin_int32);
|
||||||
|
|
||||||
|
a = iFloat64(iFloat64::kMin_int32);
|
||||||
|
EXPECT_EQ(a.s3132, iFloat64::kMin_s3132);
|
||||||
|
EXPECT_EQ(a.to_float(), iFloat64::kMin_float);
|
||||||
|
EXPECT_EQ(a.to_int32(), iFloat64::kMin_int32);
|
||||||
|
|
||||||
|
a = iFloat64(iFloat64::kMin_int32 - 1);
|
||||||
|
EXPECT_GT(a.s3132, 0);
|
||||||
|
EXPECT_GT(a.to_float(), 0);
|
||||||
|
EXPECT_GT(a.to_int32(), 0);
|
||||||
|
|
||||||
|
a = iFloat64(iFloat64::kMax_float - 100.f) + iFloat64(iFloat64::kMax_float - 200.f);
|
||||||
|
EXPECT_LT(a.s3132, 0);
|
||||||
|
EXPECT_LT(a.to_float(), 0.f);
|
||||||
|
EXPECT_LT(a.to_int32(), 0);
|
||||||
|
|
||||||
|
a = iFloat64(iFloat64::kMin_float + 100.f) - iFloat64(iFloat64::kMax_float + 200.f);
|
||||||
|
EXPECT_GT(a.s3132, 0);
|
||||||
|
EXPECT_GT(a.to_float(), 0.f);
|
||||||
|
EXPECT_GT(a.to_int32(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FixPoint64, Compare)
|
||||||
|
{
|
||||||
|
float a = _randomFloat();
|
||||||
|
iFloat64 ia = a;
|
||||||
|
EXPECT_EQ(ia.to_float(), a);
|
||||||
|
|
||||||
|
int32_t n = MathUtil::rangeRandom(iFloat64::kMin_int32, iFloat64::kMax_int32);
|
||||||
|
iFloat64 in = n;
|
||||||
|
EXPECT_EQ(in.to_int32(), n);
|
||||||
|
|
||||||
|
float f1 = _randomFloat(), f2 = f1 + 1.f;
|
||||||
|
iFloat64 iF1 = f1, iF2 = f2;
|
||||||
|
|
||||||
|
EXPECT_LT(iF1, iF2); // <
|
||||||
|
EXPECT_GT(iF2, iF1); // >
|
||||||
|
EXPECT_NE(iF1, iF2); // !=
|
||||||
|
EXPECT_EQ(iF1, iF1); // ==
|
||||||
|
|
||||||
|
iF1 += iF2;
|
||||||
|
EXPECT_EQ(iF1, f1 + f2);
|
||||||
|
|
||||||
|
iF1 -= iF2;
|
||||||
|
EXPECT_EQ(iF1, f1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FixPoint64, Operation)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < 500; i++)
|
||||||
|
{
|
||||||
|
float a = _randomFloat();
|
||||||
|
float b = _randomFloat();
|
||||||
|
|
||||||
|
iFloat64 ia(a), ib(b);
|
||||||
|
iFloat64 ret = ia + ib;
|
||||||
|
|
||||||
|
EXPECT_EQ(ret.to_float(), a + b);
|
||||||
|
|
||||||
|
iFloat64 ic(a);
|
||||||
|
iFloat64 ret2 = ic + b;
|
||||||
|
EXPECT_EQ(ret2.to_float(), a + b);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < 500; i++)
|
||||||
|
{
|
||||||
|
float a = _randomFloat();
|
||||||
|
float b = _randomFloat();
|
||||||
|
|
||||||
|
iFloat64 ia(a), ib(b);
|
||||||
|
iFloat64 ret = ia - ib;
|
||||||
|
|
||||||
|
EXPECT_EQ(ret.to_float(), a - b);
|
||||||
|
|
||||||
|
iFloat64 ic(a);
|
||||||
|
iFloat64 ret2 = ic - b;
|
||||||
|
EXPECT_EQ(ret2.to_float(), a - b);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < 500; i++)
|
||||||
|
{
|
||||||
|
float32_t a = _randomFloat();
|
||||||
|
float32_t b = _randomFloat();
|
||||||
|
int32_t c = rand() & 0xFFFF;
|
||||||
|
|
||||||
|
// iFloat*iFloat
|
||||||
|
iFloat64 ia(a), ib(b);
|
||||||
|
iFloat64 iret = ia * ib;
|
||||||
|
EXPECT_EQ(iret.to_float(), a*b);
|
||||||
|
|
||||||
|
// iFloat*int
|
||||||
|
iFloat64 iret2 = ia * c;
|
||||||
|
EXPECT_EQ(iret2.to_float(), a * c);
|
||||||
|
|
||||||
|
// iFloat*float
|
||||||
|
iFloat64 iret3 = ia * b;
|
||||||
|
EXPECT_EQ(iret3, iret);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < 500; i++)
|
||||||
|
{
|
||||||
|
float a, b, ret;
|
||||||
|
a = _randomFloat();
|
||||||
|
b = _randomFloat();
|
||||||
|
ret = a / b;
|
||||||
|
|
||||||
|
iFloat64 ia(a), ib(b);
|
||||||
|
iFloat64 iret = ia / ib;
|
||||||
|
|
||||||
|
EXPECT_LE(std::abs(iret.to_float() - ret), 0.001f);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
#include <pipe/dvc_rasterizer.h>
|
#include "pipe/dvc_rasterizer.h"
|
||||||
#include <buffer/dvu_dump_buffer.h>
|
#include "buffer/dvu_dump_buffer.h"
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
@@ -224,19 +223,19 @@ public:
|
|||||||
|
|
||||||
if (debug) {
|
if (debug) {
|
||||||
bool succ = true;
|
bool succ = true;
|
||||||
uint32_t* colBuf = new uint32_t[m_width*m_height];
|
|
||||||
|
|
||||||
for (int32_t i = 0; i < m_width*m_height; i++) {
|
for (int32_t i = 0; i < m_width*m_height; i++) {
|
||||||
colBuf[i] = m_pixelBuf[i].load();
|
|
||||||
if (m_pixelBuf[i] == 0) {
|
if (m_pixelBuf[i] == 0) {
|
||||||
if (debug) printf("error: %d,%d=%d\n", i%m_width, i/ m_width, m_pixelBuf[(size_t)i].load());
|
if (debug) printf("error: %d,%d=%d\n", i%m_width, i/ m_width, m_pixelBuf[(size_t)i].load());
|
||||||
succ = false;
|
succ = false;
|
||||||
//return false;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
davinci_utils::dump_pixel_buffer("debug.png", m_width, m_height, colBuf, PF_UINT8_RGBA);
|
if (!succ) {
|
||||||
delete[] colBuf;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return succ;
|
return succ;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -262,6 +261,27 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool dumpToPNG(const char* szFileName, bool binary)
|
||||||
|
{
|
||||||
|
uint32_t* pixelBuffer = new uint32_t[m_width * m_height];
|
||||||
|
for (int32_t i = 0; i < m_width * m_height; i++)
|
||||||
|
{
|
||||||
|
if (binary) {
|
||||||
|
pixelBuffer[i] = m_pixelBuf[i].load() != 0 ? 0xFF000000 : 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
pixelBuffer[i] = m_pixelBuf[i].load();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!davinci_utils::dump_pixel_buffer(szFileName, m_width, m_height, pixelBuffer, PF_UINT8_RGBA))
|
||||||
|
{
|
||||||
|
delete[] pixelBuffer;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
delete[] pixelBuffer;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
private:
|
private:
|
||||||
int32_t m_width, m_height;
|
int32_t m_width, m_height;
|
||||||
std::atomic<uint32_t>* m_pixelBuf;
|
std::atomic<uint32_t>* m_pixelBuf;
|
||||||
@@ -271,7 +291,7 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
//-------------------------------------------------------------------------------------
|
//-------------------------------------------------------------------------------------
|
||||||
static bool _triangleRasterizationRules(Canvas& canvas, const Triangle& triangle, const PixelBuf& pixelExpect, bool ccw=true)
|
static bool _triangleRasterizationRules(Canvas& canvas, const Triangle& triangle, const PixelBuf& pixelExpect, bool ccw=true, const char* dumpPNGName=nullptr)
|
||||||
{
|
{
|
||||||
auto razFunc = std::bind(&Canvas::razFunction_fetchadd, &canvas, std::placeholders::_1, std::placeholders::_2);
|
auto razFunc = std::bind(&Canvas::razFunction_fetchadd, &canvas, std::placeholders::_1, std::placeholders::_2);
|
||||||
|
|
||||||
@@ -281,6 +301,11 @@ static bool _triangleRasterizationRules(Canvas& canvas, const Triangle& triangle
|
|||||||
triangle.p1, triangle.p2, triangle.p3,
|
triangle.p1, triangle.p2, triangle.p3,
|
||||||
razFunc, ccw);
|
razFunc, ccw);
|
||||||
|
|
||||||
|
if (dumpPNGName != nullptr)
|
||||||
|
{
|
||||||
|
canvas.dumpToPNG(dumpPNGName, true);
|
||||||
|
}
|
||||||
|
|
||||||
return canvas.checkResult(pixelExpect);
|
return canvas.checkResult(pixelExpect);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -376,9 +401,6 @@ TEST(Rasterizer, Basic)
|
|||||||
//-------------------------------------------------------------------------------------
|
//-------------------------------------------------------------------------------------
|
||||||
TEST(Rasterizer, Delaunay)
|
TEST(Rasterizer, Delaunay)
|
||||||
{
|
{
|
||||||
//seed=1499743811, counts=5, range=0.8f;
|
|
||||||
//seed=1499670829, counts=5, range=1.f;
|
|
||||||
|
|
||||||
const int32_t width = 1024, height = 1024;
|
const int32_t width = 1024, height = 1024;
|
||||||
const int32_t verticesCounts = MathUtil::rangeRandom(5, 30);
|
const int32_t verticesCounts = MathUtil::rangeRandom(5, 30);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user