From 6dbfb5da0b262e2e40b9b1d13ad4193a93ef97ef Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 16 Jan 2013 12:51:46 -0800 Subject: [PATCH] libcore: Use LLVM intrinsics for floor; add a new Perlin noise benchmark. r=brson --- src/libcore/f32.rs | 10 +++- src/libcore/f64.rs | 12 ++++- src/test/bench/noise.rs | 110 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 129 insertions(+), 3 deletions(-) create mode 100644 src/test/bench/noise.rs diff --git a/src/libcore/f32.rs b/src/libcore/f32.rs index 4c1cc890de9..795a9f9371c 100644 --- a/src/libcore/f32.rs +++ b/src/libcore/f32.rs @@ -56,7 +56,6 @@ delegate!(fn exp2(n: c_float) -> c_float = cmath::c_float_utils::exp2) delegate!(fn abs(n: c_float) -> c_float = cmath::c_float_utils::abs) delegate!(fn abs_sub(a: c_float, b: c_float) -> c_float = cmath::c_float_utils::abs_sub) -delegate!(fn floor(n: c_float) -> c_float = cmath::c_float_utils::floor) delegate!(fn mul_add(a: c_float, b: c_float, c: c_float) -> c_float = cmath::c_float_utils::mul_add) delegate!(fn fmax(a: c_float, b: c_float) -> c_float = @@ -141,6 +140,10 @@ pub pure fn ge(x: f32, y: f32) -> bool { return x >= y; } #[inline(always)] pub pure fn gt(x: f32, y: f32) -> bool { return x > y; } +/// Returns `x` rounded down +#[inline(always)] +pub pure fn floor(x: f32) -> f32 { unsafe { floorf32(x) } } + // FIXME (#1999): replace the predicates below with llvm intrinsics or // calls to the libmath macros in the rust runtime for performance. @@ -298,6 +301,11 @@ impl f32: num::One { static pure fn one() -> f32 { 1.0 } } +#[abi="rust-intrinsic"] +pub extern { + fn floorf32(val: f32) -> f32; +} + // // Local Variables: // mode: rust diff --git a/src/libcore/f64.rs b/src/libcore/f64.rs index cbcdc2e81e6..8cd94c9357d 100644 --- a/src/libcore/f64.rs +++ b/src/libcore/f64.rs @@ -57,7 +57,6 @@ delegate!(fn exp2(n: c_double) -> c_double = cmath::c_double_utils::exp2) delegate!(fn abs(n: c_double) -> c_double = cmath::c_double_utils::abs) delegate!(fn abs_sub(a: c_double, b: c_double) -> c_double = cmath::c_double_utils::abs_sub) -delegate!(fn floor(n: c_double) -> c_double = cmath::c_double_utils::floor) delegate!(fn mul_add(a: c_double, b: c_double, c: c_double) -> c_double = cmath::c_double_utils::mul_add) delegate!(fn fmax(a: c_double, b: c_double) -> c_double = @@ -210,12 +209,16 @@ pub pure fn is_infinite(x: f64) -> bool { return x == infinity || x == neg_infinity; } -/// Returns true if `x`is a finite number +/// Returns true if `x` is a finite number #[inline(always)] pub pure fn is_finite(x: f64) -> bool { return !(is_NaN(x) || is_infinite(x)); } +/// Returns `x` rounded down +#[inline(always)] +pub pure fn floor(x: f64) -> f64 { unsafe { floorf64(x) } } + // FIXME (#1999): add is_normal, is_subnormal, and fpclassify /* Module: consts */ @@ -322,6 +325,11 @@ impl f64: num::One { static pure fn one() -> f64 { 1.0 } } +#[abi="rust-intrinsic"] +pub extern { + fn floorf64(val: f64) -> f64; +} + // // Local Variables: // mode: rust diff --git a/src/test/bench/noise.rs b/src/test/bench/noise.rs new file mode 100644 index 00000000000..a07dcee35f4 --- /dev/null +++ b/src/test/bench/noise.rs @@ -0,0 +1,110 @@ +// Perlin noise benchmark from https://gist.github.com/1170424 + +struct Vec2 { + x: f32, + y: f32, +} + +fn lerp(a: f32, b: f32, v: f32) -> f32 { a * (1.0 - v) + b * v } +fn smooth(v: f32) -> f32 { v * v * (3.0 - 2.0 * v) } + +fn random_gradient(r: rand::Rng) -> Vec2 { + let v = r.gen_float() * float::consts::pi * 2.0; + Vec2{ + x: float::cos(v) as f32, + y: float::sin(v) as f32, + } +} + +fn gradient(orig: Vec2, grad: Vec2, p: Vec2) -> f32 { + let sp = Vec2{x: p.x - orig.x, y: p.y - orig.y}; + grad.x * sp.x + grad.y + sp.y +} + +struct Noise2DContext { + rgradients: [Vec2 * 256], + permutations: [int * 256], +} + +fn Noise2DContext() -> ~Noise2DContext { + let r = rand::Rng(); + let mut rgradients = [ Vec2 { x: 0.0, y: 0.0 }, ..256 ]; + for int::range(0, 256) |i| { rgradients[i] = random_gradient(r); } + let mut permutations = [ 0, ..256 ]; + for int::range(0, 256) |i| { permutations[i] = i; } + r.shuffle_mut(permutations); + + ~Noise2DContext{ + rgradients: move rgradients, + permutations: move permutations, + } +} + +impl Noise2DContext { + #[inline(always)] + fn get_gradient(&self, x: int, y: int) -> Vec2 { + let idx = self.permutations[x & 255] + self.permutations[y & 255]; + self.rgradients[idx & 255] + } + + #[inline(always)] + fn get_gradients(&self, gradients: &mut [Vec2 * 4], origins: &mut [Vec2 * 4], x: f32, y: f32) { + let x0f = f32::floor(x); + let y0f = f32::floor(y); + let x0 = x0f as int; + let y0 = y0f as int; + let x1 = x0 + 1; + let y1 = y0 + 1; + + gradients[0] = self.get_gradient(x0, y0); + gradients[1] = self.get_gradient(x1, y0); + gradients[2] = self.get_gradient(x0, y1); + gradients[3] = self.get_gradient(x1, y1); + + origins[0] = Vec2{x: x0f + 0.0, y: y0f + 0.0}; + origins[1] = Vec2{x: x0f + 1.0, y: y0f + 0.0}; + origins[2] = Vec2{x: x0f + 0.0, y: y0f + 1.0}; + origins[3] = Vec2{x: x0f + 1.0, y: y0f + 1.0}; + } + + fn get(&self, x: f32, y: f32) -> f32 { + let p = Vec2{x: x, y: y}; + let mut gradients = [ Vec2 { x: 0.0, y: 0.0 }, ..4 ]; + let mut origins = [ Vec2 { x: 0.0, y: 0.0 }, ..4 ]; + self.get_gradients(&mut gradients, &mut origins, x, y); + let v0 = gradient(origins[0], gradients[0], p); + let v1 = gradient(origins[1], gradients[1], p); + let v2 = gradient(origins[2], gradients[2], p); + let v3 = gradient(origins[3], gradients[3], p); + let fx = smooth(x - origins[0].x); + let vx0 = lerp(v0, v1, fx); + let vx1 = lerp(v2, v3, fx); + let fy = smooth(y - origins[0].y); + lerp(vx0, vx1, fy) + } +} + +fn main() { + let symbols = [" ", "░", "▒", "▓", "█", "█"]; + let mut pixels = vec::from_elem(256*256, 0f32); + let n2d = Noise2DContext(); + for int::range(0, 100) |_| { + for int::range(0, 256) |y| { + for int::range(0, 256) |x| { + let v = n2d.get( + x as f32 * 0.1f32, + y as f32 * 0.1f32 + ) * 0.5f32 + 0.5f32; + pixels[y*256+x] = v; + }; + }; + }; + + /*for int::range(0, 256) |y| { + for int::range(0, 256) |x| { + io::print(symbols[pixels[y*256+x] / 0.2f32 as int]); + } + io::println(""); + }*/ +} +