diff --git a/jme3-core/src/main/java/com/jme3/math/FastMath.java b/jme3-core/src/main/java/com/jme3/math/FastMath.java index d7b533fbd9..bb213c8ffc 100644 --- a/jme3-core/src/main/java/com/jme3/math/FastMath.java +++ b/jme3-core/src/main/java/com/jme3/math/FastMath.java @@ -1203,10 +1203,44 @@ public static float unInterpolateLinear(float value, float min, float max) { } /** - * Round n to a multiple of p + * Align n to a multiple of p, where p must be a power of two. + * This is an optimized version that uses bitwise operations. + * + * @param n the value to align + * @param p the alignment value (must be a power of two) + * @return the smallest value greater than or equal to n that is a multiple of p + * @throws IllegalArgumentException if p is not a power of two + */ + public static int alignToPowerOfTwo(int n, int p) { + if (!isPowerOfTwo(p)) { + throw new IllegalArgumentException("p must be a power of two, got: " + p); + } + return ((n - 1) | (p - 1)) + 1; + } + + /** + * Round n up to the nearest multiple of p. + * This works for any positive integer p. + * + * @param n the value to round + * @param p the multiple (must be positive) + * @return the smallest value greater than or equal to n that is a multiple of p + * @throws IllegalArgumentException if p is not positive */ public static int toMultipleOf(int n, int p) { - return ((n - 1) | (p - 1)) + 1; + if (p <= 0) { + throw new IllegalArgumentException("p must be positive, got: " + p); + } + // For powers of two, use the optimized version + if (isPowerOfTwo(p)) { + return ((n - 1) | (p - 1)) + 1; + } + // Generic implementation for any positive integer + int remainder = n % p; + if (remainder == 0) { + return n; + } + return n + (p - remainder); } } diff --git a/jme3-core/src/main/java/com/jme3/shader/bufferobject/layout/BufferLayout.java b/jme3-core/src/main/java/com/jme3/shader/bufferobject/layout/BufferLayout.java index f8edf9a203..84b0b9ed24 100644 --- a/jme3-core/src/main/java/com/jme3/shader/bufferobject/layout/BufferLayout.java +++ b/jme3-core/src/main/java/com/jme3/shader/bufferobject/layout/BufferLayout.java @@ -120,11 +120,11 @@ public int getBasicAlignment(Object o) { * @param pos * the position to align * @param basicAlignment - * the basic alignment + * the basic alignment (must be a power of two) * @return the aligned position */ public int align(int pos, int basicAlignment) { - return pos==0?pos:FastMath.toMultipleOf(pos, basicAlignment); + return pos==0?pos:FastMath.alignToPowerOfTwo(pos, basicAlignment); } /** diff --git a/jme3-core/src/test/java/com/jme3/math/FastMathTest.java b/jme3-core/src/test/java/com/jme3/math/FastMathTest.java index 13f8ba6e8a..b99f5836eb 100644 --- a/jme3-core/src/test/java/com/jme3/math/FastMathTest.java +++ b/jme3-core/src/test/java/com/jme3/math/FastMathTest.java @@ -855,4 +855,78 @@ public void testSphericalToCartesianZ() { assertEquals(0.0f, retval.getY(), 0.0f); assertEquals(0.0f, retval.getZ(), 0.0f); } + + @Test + public void testAlignToPowerOfTwo() { + // Test with various power-of-two values + assertEquals(8, FastMath.alignToPowerOfTwo(5, 4)); + assertEquals(8, FastMath.alignToPowerOfTwo(8, 4)); + assertEquals(16, FastMath.alignToPowerOfTwo(15, 16)); + assertEquals(16, FastMath.alignToPowerOfTwo(16, 16)); + assertEquals(32, FastMath.alignToPowerOfTwo(17, 16)); + assertEquals(1, FastMath.alignToPowerOfTwo(1, 1)); + assertEquals(2, FastMath.alignToPowerOfTwo(2, 2)); + assertEquals(4, FastMath.alignToPowerOfTwo(3, 4)); + assertEquals(64, FastMath.alignToPowerOfTwo(50, 64)); + assertEquals(128, FastMath.alignToPowerOfTwo(100, 128)); + + // Test edge cases + assertEquals(256, FastMath.alignToPowerOfTwo(256, 256)); + assertEquals(512, FastMath.alignToPowerOfTwo(257, 256)); + + // Test that it throws for non-power-of-two values + try { + FastMath.alignToPowerOfTwo(10, 3); + fail("Expected IllegalArgumentException for non-power-of-two"); + } catch (IllegalArgumentException e) { + // Expected + } + + try { + FastMath.alignToPowerOfTwo(10, 6); + fail("Expected IllegalArgumentException for non-power-of-two"); + } catch (IllegalArgumentException e) { + // Expected + } + } + + @Test + public void testToMultipleOf() { + // Test with various values including non-powers-of-two + assertEquals(6, FastMath.toMultipleOf(5, 3)); + assertEquals(6, FastMath.toMultipleOf(6, 3)); + assertEquals(9, FastMath.toMultipleOf(7, 3)); + assertEquals(10, FastMath.toMultipleOf(10, 5)); + assertEquals(15, FastMath.toMultipleOf(11, 5)); + assertEquals(7, FastMath.toMultipleOf(5, 7)); + assertEquals(7, FastMath.toMultipleOf(7, 7)); + assertEquals(14, FastMath.toMultipleOf(8, 7)); + + // Test with power-of-two values (should work too) + assertEquals(8, FastMath.toMultipleOf(5, 4)); + assertEquals(8, FastMath.toMultipleOf(8, 4)); + assertEquals(16, FastMath.toMultipleOf(15, 16)); + assertEquals(16, FastMath.toMultipleOf(16, 16)); + assertEquals(32, FastMath.toMultipleOf(17, 16)); + + // Test edge cases + assertEquals(1, FastMath.toMultipleOf(1, 1)); + assertEquals(100, FastMath.toMultipleOf(100, 100)); + assertEquals(200, FastMath.toMultipleOf(101, 100)); + + // Test that it throws for non-positive values + try { + FastMath.toMultipleOf(10, 0); + fail("Expected IllegalArgumentException for p=0"); + } catch (IllegalArgumentException e) { + // Expected + } + + try { + FastMath.toMultipleOf(10, -5); + fail("Expected IllegalArgumentException for negative p"); + } catch (IllegalArgumentException e) { + // Expected + } + } }