Преглед на файлове

Enable floating point overflow exceptions (#1398)

This helps find programming errors.
Jorrit Rouwe преди 8 месеца
родител
ревизия
fe87468daa

+ 20 - 6
Jolt/Core/FPException.h

@@ -16,11 +16,12 @@ JPH_NAMESPACE_BEGIN
 class FPExceptionsEnable { };
 class FPExceptionDisableInvalid { };
 class FPExceptionDisableDivByZero { };
+class FPExceptionDisableOverflow { };
 
 #elif defined(JPH_USE_SSE)
 
-/// Enable floating point divide by zero exception and exceptions on invalid numbers
-class FPExceptionsEnable : public FPControlWord<0, _MM_MASK_DIV_ZERO | _MM_MASK_INVALID> { };
+/// Enable floating point divide by zero exception, overflow exceptions and exceptions on invalid numbers
+class FPExceptionsEnable : public FPControlWord<0, _MM_MASK_DIV_ZERO | _MM_MASK_INVALID | _MM_MASK_OVERFLOW> { };
 
 /// Disable invalid floating point value exceptions
 class FPExceptionDisableInvalid : public FPControlWord<_MM_MASK_INVALID, _MM_MASK_INVALID> { };
@@ -28,10 +29,13 @@ class FPExceptionDisableInvalid : public FPControlWord<_MM_MASK_INVALID, _MM_MAS
 /// Disable division by zero floating point exceptions
 class FPExceptionDisableDivByZero : public FPControlWord<_MM_MASK_DIV_ZERO, _MM_MASK_DIV_ZERO> { };
 
+/// Disable floating point overflow exceptions
+class FPExceptionDisableOverflow : public FPControlWord<_MM_MASK_OVERFLOW, _MM_MASK_OVERFLOW> { };
+
 #elif defined(JPH_CPU_ARM) && defined(JPH_COMPILER_MSVC)
 
-/// Enable floating point divide by zero exception and exceptions on invalid numbers
-class FPExceptionsEnable : public FPControlWord<0, _EM_INVALID | _EM_ZERODIVIDE> { };
+/// Enable floating point divide by zero exception, overflow exceptions and exceptions on invalid numbers
+class FPExceptionsEnable : public FPControlWord<0, _EM_INVALID | _EM_ZERODIVIDE | _EM_OVERFLOW> { };
 
 /// Disable invalid floating point value exceptions
 class FPExceptionDisableInvalid : public FPControlWord<_EM_INVALID, _EM_INVALID> { };
@@ -39,6 +43,9 @@ class FPExceptionDisableInvalid : public FPControlWord<_EM_INVALID, _EM_INVALID>
 /// Disable division by zero floating point exceptions
 class FPExceptionDisableDivByZero : public FPControlWord<_EM_ZERODIVIDE, _EM_ZERODIVIDE> { };
 
+/// Disable floating point overflow exceptions
+class FPExceptionDisableOverflow : public FPControlWord<_EM_OVERFLOW, _EM_OVERFLOW> { };
+
 #elif defined(JPH_CPU_ARM)
 
 /// Invalid operation exception bit
@@ -47,8 +54,11 @@ static constexpr uint64 FP_IOE = 1 << 8;
 /// Enable divide by zero exception bit
 static constexpr uint64 FP_DZE = 1 << 9;
 
-/// Enable floating point divide by zero exception and exceptions on invalid numbers
-class FPExceptionsEnable : public FPControlWord<FP_IOE | FP_DZE, FP_IOE | FP_DZE> { };
+/// Enable floating point overflow bit
+static constexpr uint64 FP_OFE = 1 << 10;
+
+/// Enable floating point divide by zero exception, overflow exceptions and exceptions on invalid numbers
+class FPExceptionsEnable : public FPControlWord<FP_IOE | FP_DZE | FP_OFE, FP_IOE | FP_DZE | FP_OFE> { };
 
 /// Disable invalid floating point value exceptions
 class FPExceptionDisableInvalid : public FPControlWord<0, FP_IOE> { };
@@ -56,6 +66,9 @@ class FPExceptionDisableInvalid : public FPControlWord<0, FP_IOE> { };
 /// Disable division by zero floating point exceptions
 class FPExceptionDisableDivByZero : public FPControlWord<0, FP_DZE> { };
 
+/// Disable floating point overflow exceptions
+class FPExceptionDisableOverflow : public FPControlWord<0, FP_OFE> { };
+
 #elif defined(JPH_CPU_RISCV)
 
 #error "RISC-V only implements manually checking if exceptions occurred by reading the fcsr register. It doesn't generate exceptions. JPH_FLOATING_POINT_EXCEPTIONS_ENABLED must be disabled."
@@ -76,6 +89,7 @@ class FPExceptionDisableDivByZero : public FPControlWord<0, FP_DZE> { };
 class FPExceptionsEnable { };
 class FPExceptionDisableInvalid { };
 class FPExceptionDisableDivByZero { };
+class FPExceptionDisableOverflow { };
 
 #endif
 

+ 3 - 2
Jolt/Geometry/AABox.h

@@ -34,10 +34,11 @@ public:
 		return box;
 	}
 
-	/// Get bounding box of size 2 * FLT_MAX
+	/// Get bounding box of size FLT_MAX
 	static AABox	sBiggest()
 	{
-		return AABox(Vec3::sReplicate(-FLT_MAX), Vec3::sReplicate(FLT_MAX));
+		/// Max half extent of AABox is 0.5 * FLT_MAX so that GetSize() remains finite
+		return AABox(Vec3::sReplicate(-0.5f * FLT_MAX), Vec3::sReplicate(0.5f * FLT_MAX));
 	}
 
 	/// Comparison operators

+ 1 - 1
Jolt/Math/EigenValueSymmetric.h

@@ -30,7 +30,7 @@ bool EigenValueSymmetric(const Matrix &inMatrix, Matrix &outEigVec, Vector &outE
 {
 	// This algorithm can generate infinite values, see comment below
 	FPExceptionDisableInvalid disable_invalid;
-	(void)disable_invalid;
+	JPH_UNUSED(disable_invalid);
 
 	// Maximum number of sweeps to make
 	const int cMaxSweeps = 50;

+ 4 - 0
Jolt/Math/HalfFloat.h

@@ -5,6 +5,7 @@
 #pragma once
 
 #include <Jolt/Math/Vec4.h>
+#include <Jolt/Core/FPException.h>
 
 JPH_NAMESPACE_BEGIN
 
@@ -132,6 +133,9 @@ template <int RoundingMode>
 JPH_INLINE HalfFloat FromFloat(float inV)
 {
 #ifdef JPH_USE_F16C
+	FPExceptionDisableOverflow disable_overflow;
+	JPH_UNUSED(disable_overflow);
+
 	union
 	{
 		__m128i		u128;

+ 3 - 0
Jolt/Math/Math.h

@@ -9,6 +9,9 @@ JPH_NAMESPACE_BEGIN
 /// The constant \f$\pi\f$
 static constexpr float JPH_PI = 3.14159265358979323846f;
 
+/// A large floating point value which, when squared, is still much smaller than FLT_MAX
+static constexpr float cLargeFloat = 1.0e15f;
+
 /// Convert a value from degrees to radians
 JPH_INLINE constexpr float DegreesToRadians(float inV)
 {

+ 0 - 1
Jolt/Physics/Collision/BroadPhase/QuadTree.cpp

@@ -110,7 +110,6 @@ bool QuadTree::Node::EncapsulateChildBounds(int inChildIndex, const AABox &inBou
 // QuadTree
 ////////////////////////////////////////////////////////////////////////////////////////////////////////
 
-const float QuadTree::cLargeFloat = 1.0e30f;
 const AABox QuadTree::cInvalidBounds(Vec3::sReplicate(cLargeFloat), Vec3::sReplicate(-cLargeFloat));
 
 void QuadTree::GetBodyLocation(const TrackingVector &inTracking, BodyID inBodyID, uint32 &outNodeIdx, uint32 &outChildIdx) const

+ 0 - 1
Jolt/Physics/Collision/BroadPhase/QuadTree.h

@@ -262,7 +262,6 @@ public:
 private:
 	/// Constants
 	static const uint32			cInvalidNodeIndex = 0xffffffff;		///< Value used to indicate node index is invalid
-	static const float			cLargeFloat;						///< A large floating point number that is small enough to not cause any overflows
 	static const AABox			cInvalidBounds;						///< Invalid bounding box using cLargeFloat
 
 	/// We alternate between two trees in order to let collision queries complete in parallel to adding/removing objects to the tree

+ 2 - 2
Jolt/Physics/Collision/Shape/HeightFieldShape.cpp

@@ -1869,8 +1869,8 @@ public:
 					uint32 stride = block_size_plus_1 - size_x_plus_1;
 
 					// Start range with a very large inside-out box
-					Vec3 value_min = Vec3::sReplicate(1.0e30f);
-					Vec3 value_max = Vec3::sReplicate(-1.0e30f);
+					Vec3 value_min = Vec3::sReplicate(cLargeFloat);
+					Vec3 value_max = Vec3::sReplicate(-cLargeFloat);
 
 					// Loop over the samples to determine the min and max of this block
 					for (uint32 block_y = 0; block_y < size_y_plus_1; ++block_y)

+ 2 - 2
Jolt/Physics/Collision/Shape/HeightFieldShape.h

@@ -73,10 +73,10 @@ public:
 	uint32							mSampleCount = 0;
 
 	/// Artificial minimal value of mHeightSamples, used for compression and can be used to update the terrain after creating with lower height values. If there are any lower values in mHeightSamples, this value will be ignored.
-	float							mMinHeightValue = FLT_MAX;
+	float							mMinHeightValue = cLargeFloat;
 
 	/// Artificial maximum value of mHeightSamples, used for compression and can be used to update the terrain after creating with higher height values. If there are any higher values in mHeightSamples, this value will be ignored.
-	float							mMaxHeightValue = -FLT_MAX;
+	float							mMaxHeightValue = -cLargeFloat;
 
 	/// When bigger than mMaterials.size() the internal material list will be preallocated to support this number of materials.
 	/// This avoids reallocations when calling HeightFieldShape::SetMaterials with new materials later.

+ 4 - 0
Jolt/Physics/SoftBody/SoftBodySharedSettings.cpp

@@ -541,6 +541,10 @@ void SoftBodySharedSettings::Optimize(OptimizationResults &outResults)
 			if (group_idx[i] == -1)
 				bounds.Encapsulate(Vec3(mVertices[i].mPosition));
 
+		// If the bounds are invalid, it means that there were no ungrouped vertices
+		if (!bounds.IsValid())
+			break;
+
 		// Determine longest and shortest axis
 		Vec3 bounds_size = bounds.GetSize();
 		uint max_axis = bounds_size.GetHighestComponentIndex();

+ 1 - 1
Jolt/Renderer/DebugRenderer.cpp

@@ -18,7 +18,7 @@ DebugRenderer *DebugRenderer::sInstance = nullptr;
 static const int sMaxLevel = 4;
 
 // Distance for each LOD level, these are tweaked for an object of approx. size 1. Use the lod scale to scale these distances.
-static const float sLODDistanceForLevel[] = { 5.0f, 10.0f, 40.0f, FLT_MAX };
+static const float sLODDistanceForLevel[] = { 5.0f, 10.0f, 40.0f, cLargeFloat };
 
 DebugRenderer::Triangle::Triangle(Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inV3, ColorArg inColor)
 {

+ 1 - 1
Jolt/Renderer/DebugRenderer.h

@@ -218,7 +218,7 @@ public:
 
 		/// Constructor
 							Geometry(const AABox &inBounds) : mBounds(inBounds) { }
-							Geometry(const Batch &inBatch, const AABox &inBounds) : mBounds(inBounds) { mLODs.push_back({ inBatch, FLT_MAX }); }
+							Geometry(const Batch &inBatch, const AABox &inBounds) : mBounds(inBounds) { mLODs.push_back({ inBatch, cLargeFloat }); }
 
 		/// Determine which LOD to render
 		/// @param inCameraPosition Current position of the camera

+ 2 - 2
UnitTests/Geometry/GJKTests.cpp

@@ -51,7 +51,7 @@ TEST_SUITE("GJKTests")
 		{
 			// Test sphere s1 and s2, they should not collide, verify their closest points
 			Vec3 pa, pb, v = Vec3::sZero();
-			float d = sqrt(gjk.GetClosestPoints(s1, s2, 1.0e-4f, FLT_MAX, v, pa, pb));
+			float d = sqrt(gjk.GetClosestPoints(s1, s2, 1.0e-4f, cLargeFloat, v, pa, pb));
 			CHECK_APPROX_EQUAL(c2.Length() - 2.0f, d, 1.0e-4f);
 			CHECK_APPROX_EQUAL(c2.Normalized(), pa, 1.0e-4f);
 			CHECK_APPROX_EQUAL(c2 - c2.Normalized(), pb, 1.0e-4f);
@@ -60,7 +60,7 @@ TEST_SUITE("GJKTests")
 		{
 			// Test sphere s1 and s3, they should touch exactly, verify their closest points
 			Vec3 pa, pb, v = Vec3::sZero();
-			float d = sqrt(gjk.GetClosestPoints(s1, s3, 1.0e-4f, FLT_MAX, v, pa, pb));
+			float d = sqrt(gjk.GetClosestPoints(s1, s3, 1.0e-4f, cLargeFloat, v, pa, pb));
 			CHECK_APPROX_EQUAL(0.0f, d, 1.0e-4f);
 			CHECK_APPROX_EQUAL(c2.Normalized(), pa, 1.0e-4f);
 			CHECK_APPROX_EQUAL(c2.Normalized(), pb, 1.0e-4f);