Skip to content
Open
2 changes: 2 additions & 0 deletions SConstruct
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,7 @@ if env["arch"] == "x86_64":
env.Append(CCFLAGS=["/d2archSSE42"])
else:
# `-msse2` is implied when compiling for x86_64.
# core/math/simd.h relies on SSE4.1 intrinsics being available on x86_64 (clang-cl included).
env.Append(CCFLAGS=["-msse4.2", "-mpopcnt"])
elif env["arch"] == "x86_32":
# Be more conservative with instruction sets on 32-bit x86 to improve compatibility.
Expand Down Expand Up @@ -852,6 +853,7 @@ if env.msvc and not methods.using_clang(env): # MSVC
"/wd4245",
"/wd4267",
"/wd4305", # C4305 (truncation): double to float or real_t, too hard to avoid.
"/wd4702", # C4702 (unreachable code): compile-time SIMD/scalar dual paths leave one branch unreachable.
"/wd4324", # C4820 (structure was padded due to alignment specifier)
"/wd4514", # C4514 (unreferenced inline function has been removed)
"/wd4714", # C4714 (function marked as __forceinline not inlined)
Expand Down
561 changes: 533 additions & 28 deletions core/io/image.cpp

Large diffs are not rendered by default.

23 changes: 23 additions & 0 deletions core/math/aabb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,29 @@ bool AABB::intersects_segment(const Vector3 &p_from, const Vector3 &p_to, Vector
}

bool AABB::intersects_plane(const Plane &p_plane) const {
#ifdef GODOT_SIMD_REAL_F32X4
{
// Corners are evaluated 4 at a time with the x coordinate fixed per group; the
// distance is (nx*x + ny*y) + nz*z - d, the same order as Plane::distance_to.
using namespace simd;
f32x4 pos = _aabb_position_simd(*this);
f32x4 end = _aabb_end_simd(*this);
f32x4 ys = shuffle<1, 1, 1, 1>(pos, end);
f32x4 zs = shuffle<2, 2, 2, 2>(pos, end);
zs = permute<0, 2, 1, 3>(zs);
f32x4 nx = splat4(p_plane.normal.x);
f32x4 ny = splat4(p_plane.normal.y);
f32x4 nz = splat4(p_plane.normal.z);
f32x4 d = splat4(p_plane.d);
f32x4 dist_a = ((nx * splat_lane<0>(pos) + ny * ys) + nz * zs) - d;
f32x4 dist_b = ((nx * splat_lane<0>(end) + ny * ys) + nz * zs) - d;
int over_a = movemask(cmp_gt(dist_a, zero4()));
int over_b = movemask(cmp_gt(dist_b, zero4()));
bool over = (over_a | over_b) != 0;
bool under = over_a != 0xF || over_b != 0xF;
return under && over;
}
#endif
Vector3 points[8] = {
Vector3(position.x, position.y, position.z),
Vector3(position.x, position.y, position.z + size.z),
Expand Down
96 changes: 96 additions & 0 deletions core/math/aabb.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
*/

#include "core/math/plane.h"
#include "core/math/simd.h"
#include "core/math/vector3.h"

class Variant;
Expand Down Expand Up @@ -144,6 +145,19 @@ struct [[nodiscard]] AABB {
}
};

#ifdef GODOT_SIMD_REAL_F32X4
// Load the three components without reading beyond either Vector3; lane 3 is zero.
_FORCE_INLINE_ simd::f32x4 _aabb_position_simd(const AABB &p_aabb) {
return simd::load3(&p_aabb.position.x);
}
_FORCE_INLINE_ simd::f32x4 _aabb_size_simd(const AABB &p_aabb) {
return simd::load3(&p_aabb.size.x);
}
_FORCE_INLINE_ simd::f32x4 _aabb_end_simd(const AABB &p_aabb) {
return _aabb_position_simd(p_aabb) + _aabb_size_simd(p_aabb);
}
#endif

inline bool AABB::intersects(const AABB &p_aabb) const {
#ifdef MATH_CHECKS
if (unlikely(size.x < 0 || size.y < 0 || size.z < 0 || p_aabb.size.x < 0 || p_aabb.size.y < 0 || p_aabb.size.z < 0)) {
Expand Down Expand Up @@ -258,6 +272,34 @@ Vector3 AABB::get_endpoint(int p_point) const {
}

bool AABB::intersects_convex_shape(const Plane *p_planes, int p_plane_count, const Vector3 *p_points, int p_point_count) const {
#ifdef GODOT_SIMD_REAL_F32X4
{
using namespace simd;
f32x4 half_extents = _aabb_size_simd(*this) * splat4(0.5f);
f32x4 ofs = _aabb_position_simd(*this) + half_extents;
f32x4 neg_half_extents = -half_extents;
for (int i = 0; i < p_plane_count; i++) {
// Plane is normal followed by d, so a 4-lane load holds d in lane 3.
f32x4 plane = load4(&p_planes[i].normal.x);
f32x4 point = select(cmp_gt(plane, zero4()), neg_half_extents, half_extents) + ofs;
if (dot3(plane, point) > get_lane<3>(plane)) {
return false;
}
}
// Make sure all points in the shape aren't fully separated from the AABB on each axis.
f32x4 upper = ofs + half_extents;
f32x4 lower = ofs - half_extents;
i32x4 bad_positive = zero_i32x4();
i32x4 bad_negative = zero_i32x4();
for (int i = 0; i < p_point_count; i++) {
f32x4 p = load3(&p_points[i].x);
bad_positive = count_if(bad_positive, cmp_gt(p, upper));
bad_negative = count_if(bad_negative, cmp_lt(p, lower));
}
mask4 separated = cmp_eq(bad_positive, p_point_count) | cmp_eq(bad_negative, p_point_count);
return !any_lane3(separated);
}
#endif
Vector3 half_extents = size * 0.5f;
Vector3 ofs = position + half_extents;

Expand Down Expand Up @@ -299,6 +341,22 @@ bool AABB::intersects_convex_shape(const Plane *p_planes, int p_plane_count, con
}

bool AABB::inside_convex_shape(const Plane *p_planes, int p_plane_count) const {
#ifdef GODOT_SIMD_REAL_F32X4
{
using namespace simd;
f32x4 half_extents = _aabb_size_simd(*this) * splat4(0.5f);
f32x4 ofs = _aabb_position_simd(*this) + half_extents;
f32x4 neg_half_extents = -half_extents;
for (int i = 0; i < p_plane_count; i++) {
f32x4 plane = load4(&p_planes[i].normal.x);
f32x4 point = select(cmp_lt(plane, zero4()), neg_half_extents, half_extents) + ofs;
if (dot3(plane, point) > get_lane<3>(plane)) {
return false;
}
}
return true;
}
#endif
Vector3 half_extents = size * 0.5f;
Vector3 ofs = position + half_extents;

Expand Down Expand Up @@ -422,6 +480,43 @@ bool AABB::smits_intersect_ray(const Vector3 &p_from, const Vector3 &p_dir, real
ERR_PRINT("AABB size is negative, this is not supported. Use AABB.abs() to get an AABB with a positive size.");
}
#endif
#ifdef GODOT_SIMD_REAL_F32X4
using namespace simd;
// The unused lane must have a nonzero divisor to avoid spurious floating-point exceptions.
f32x4 dir = insert_lane3(load3(&p_dir.x), splat4(1.0f));
f32x4 div = splat4(1.0f) / dir;
f32x4 from = load3(&p_from.x);
f32x4 ta = (_aabb_position_simd(*this) - from) * div;
f32x4 tb = (_aabb_end_simd(*this) - from) * div;
mask4 positive = cmp_ge(dir, zero4());
f32x4 tmin_v = select(positive, ta, tb);
f32x4 tmax_v = select(positive, tb, ta);
real_t tmin = get_lane<0>(tmin_v);
real_t tmax = get_lane<0>(tmax_v);
real_t tymin = get_lane<1>(tmin_v);
real_t tymax = get_lane<1>(tmax_v);
if ((tmin > tymax) || (tymin > tmax)) {
return false;
}
if (tymin > tmin) {
tmin = tymin;
}
if (tymax < tmax) {
tmax = tymax;
}
real_t tzmin = get_lane<2>(tmin_v);
real_t tzmax = get_lane<2>(tmax_v);
if ((tmin > tzmax) || (tzmin > tmax)) {
return false;
}
if (tzmin > tmin) {
tmin = tzmin;
}
if (tzmax < tmax) {
tmax = tzmax;
}
return ((tmin < p_t1) && (tmax > p_t0));
#else
real_t divx = 1.0f / p_dir.x;
real_t divy = 1.0f / p_dir.y;
real_t divz = 1.0f / p_dir.z;
Expand Down Expand Up @@ -468,6 +563,7 @@ bool AABB::smits_intersect_ray(const Vector3 &p_from, const Vector3 &p_dir, real
tmax = tzmax;
}
return ((tmin < p_t1) && (tmax > p_t0));
#endif
}

void AABB::grow_by(real_t p_amount) {
Expand Down
11 changes: 11 additions & 0 deletions core/math/basis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include "basis.h"

#include "core/math/math_funcs.h"
#include "core/math/simd.h"
#include "core/string/ustring.h"

#define cofac(row1, col1, row2, col2) \
Expand Down Expand Up @@ -279,6 +280,16 @@ Basis Basis::scaled_local(const Vector3 &p_scale) const {
}

Vector3 Basis::get_scale_abs() const {
#ifdef GODOT_SIMD_REAL_F32X4
{
using namespace simd;
f32x4 r0, r1, r2;
load_mat3(&rows[0].x, r0, r1, r2);
Vector3 ret;
store3(&ret.x, sqrt4(r0 * r0 + r1 * r1 + r2 * r2));
return ret;
}
#endif
return Vector3(
Vector3(rows[0][0], rows[1][0], rows[2][0]).length(),
Vector3(rows[0][1], rows[1][1], rows[2][1]).length(),
Expand Down
47 changes: 45 additions & 2 deletions core/math/bvh_abb.h
Original file line number Diff line number Diff line change
Expand Up @@ -223,8 +223,33 @@ struct BVH_ABB {
return true;
}

#ifdef GODOT_SIMD_REAL_F32X4
// 3D: min and neg_max are 6 contiguous floats; 2D: 4 contiguous floats (min.x min.y neg_max.x neg_max.y).
static constexpr bool SIMD_3D = (POINT::AXIS_COUNT == 3) && std::is_same_v<decltype(POINT::x), float>;
static constexpr bool SIMD_2D = (POINT::AXIS_COUNT == 2) && std::is_same_v<decltype(POINT::x), float>;
_FORCE_INLINE_ simd::f32x4 _simd_min() const {
return simd::load3(&min[0]);
}
_FORCE_INLINE_ simd::f32x4 _simd_neg_max() const {
return simd::load3(&neg_max[0]);
}
#endif

/// @warning Very hot in profiling, make sure optimized
bool intersects(const BVH_ABB &p_o) const {
#ifdef GODOT_SIMD_REAL_F32X4
if constexpr (SIMD_3D) {
using namespace simd;
mask4 m = cmp_gt(p_o._simd_min(), -_simd_neg_max()) | cmp_gt(_simd_min(), -p_o._simd_neg_max());
return !any_lane3(m);
} else if constexpr (SIMD_2D) {
using namespace simd;
f32x4 a = load4(&min[0]); // min.x min.y nmax.x nmax.y
f32x4 b = load4(&p_o.min[0]);
mask4 m = cmp_gt(movelh(b, a), -movehl(b, a));
return !any_lane(m);
}
#endif
if (_any_morethan(p_o.min, -neg_max)) {
return false;
}
Expand All @@ -233,9 +258,18 @@ struct BVH_ABB {
}
return true;
}

/// For pre-swizzled tester (this object)
bool intersects_swizzled(const BVH_ABB &p_o) const {
#ifdef GODOT_SIMD_REAL_F32X4
if constexpr (SIMD_3D) {
using namespace simd;
mask4 m = cmp_lt(_simd_min(), p_o._simd_min()) | cmp_lt(_simd_neg_max(), p_o._simd_neg_max());
return !any_lane3(m);
} else if constexpr (SIMD_2D) {
using namespace simd;
return !any_lane(cmp_lt(load4(&min[0]), load4(&p_o.min[0])));
}
#endif
if (_any_lessthan(min, p_o.min)) {
return false;
}
Expand All @@ -244,8 +278,17 @@ struct BVH_ABB {
}
return true;
}

bool is_other_within(const BVH_ABB &p_o) const {
#ifdef GODOT_SIMD_REAL_F32X4
if constexpr (SIMD_3D) {
using namespace simd;
mask4 m = cmp_lt(p_o._simd_neg_max(), _simd_neg_max()) | cmp_lt(p_o._simd_min(), _simd_min());
return !any_lane3(m);
} else if constexpr (SIMD_2D) {
using namespace simd;
return !any_lane(cmp_lt(load4(&p_o.min[0]), load4(&min[0])));
}
#endif
if (_any_lessthan(p_o.neg_max, neg_max)) {
return false;
}
Expand Down
Loading
Loading