diff --git a/Cargo.lock b/Cargo.lock index f4bfe3e..c27ad63 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -134,6 +134,15 @@ dependencies = [ name = "color" version = "0.1.0" +[[package]] +name = "compact" +version = "0.1.0" +dependencies = [ + "bencher", + "proptest", + "rand 0.6.5", +] + [[package]] name = "copy_in_place" version = "0.2.1" @@ -277,15 +286,6 @@ dependencies = [ "libc", ] -[[package]] -name = "oct32norm" -version = "0.1.0" -dependencies = [ - "bencher", - "proptest", - "rand 0.6.5", -] - [[package]] name = "openexr" version = "0.6.0" @@ -352,6 +352,7 @@ dependencies = [ "bvh_order", "clap", "color", + "compact", "copy_in_place", "crossbeam", "fastapprox", @@ -363,7 +364,6 @@ dependencies = [ "math3d", "nom", "num_cpus", - "oct32norm", "openexr", "png_encode_mini", "rustc-serialize", @@ -371,7 +371,6 @@ dependencies = [ "sobol", "spectral_upsampling", "time", - "trifloat", ] [[package]] @@ -663,14 +662,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "trifloat" -version = "0.1.0" -dependencies = [ - "bencher", - "rand 0.6.5", -] - [[package]] name = "unicode-width" version = "0.1.8" diff --git a/Cargo.toml b/Cargo.toml index 082fb92..ee4865c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,12 +2,11 @@ members = [ "sub_crates/bvh_order", "sub_crates/color", + "sub_crates/compact", "sub_crates/halton", "sub_crates/math3d", - "sub_crates/oct32norm", "sub_crates/sobol", "sub_crates/spectral_upsampling", - "sub_crates/trifloat" ] [package] @@ -15,6 +14,7 @@ name = "psychopath" version = "0.1.0" authors = ["Nathan Vegdahl "] edition = "2018" +license = "GPL v3" [profile.release] debug = true @@ -45,20 +45,17 @@ path = "sub_crates/bvh_order" [dependencies.color] path = "sub_crates/color" +[dependencies.compact] +path = "sub_crates/compact" [dependencies.halton] + path = "sub_crates/halton" [dependencies.math3d] path = "sub_crates/math3d" -[dependencies.oct32norm] -path = "sub_crates/oct32norm" - [dependencies.sobol] path = "sub_crates/sobol" [dependencies.spectral_upsampling] path = "sub_crates/spectral_upsampling" - -[dependencies.trifloat] -path = "sub_crates/trifloat" \ No newline at end of file diff --git a/src/color.rs b/src/color.rs index 14b8498..b190e1d 100644 --- a/src/color.rs +++ b/src/color.rs @@ -4,10 +4,10 @@ pub use color::{ rec709_e_to_xyz, rec709_to_xyz, xyz_to_aces_ap0, xyz_to_aces_ap0_e, xyz_to_rec709, xyz_to_rec709_e, }; +use compact::fluv::fluv32; use glam::Vec4; use half::f16; use spectral_upsampling::meng::{spectrum_xyz_to_p_4, EQUAL_ENERGY_REFLECTANCE}; -use trifloat::fluv32; use crate::{lerp::Lerp, math::fast_exp}; diff --git a/sub_crates/bvh_order/Cargo.toml b/sub_crates/bvh_order/Cargo.toml index e607964..9b81822 100644 --- a/sub_crates/bvh_order/Cargo.toml +++ b/sub_crates/bvh_order/Cargo.toml @@ -3,7 +3,7 @@ name = "bvh_order" version = "0.1.0" authors = ["Nathan Vegdahl "] edition = "2018" -license = "MIT" +license = "MIT, Apache 2.0" build = "build.rs" [lib] diff --git a/sub_crates/color/Cargo.toml b/sub_crates/color/Cargo.toml index 78f8d35..8634bbb 100644 --- a/sub_crates/color/Cargo.toml +++ b/sub_crates/color/Cargo.toml @@ -3,7 +3,7 @@ name = "color" version = "0.1.0" authors = ["Nathan Vegdahl "] edition = "2018" -license = "MIT" +license = "MIT, Apache 2.0" build = "build.rs" [lib] diff --git a/sub_crates/oct32norm/Cargo.toml b/sub_crates/compact/Cargo.toml similarity index 78% rename from sub_crates/oct32norm/Cargo.toml rename to sub_crates/compact/Cargo.toml index 294ac5a..e422b10 100644 --- a/sub_crates/oct32norm/Cargo.toml +++ b/sub_crates/compact/Cargo.toml @@ -1,12 +1,12 @@ [package] -name = "oct32norm" +name = "compact" version = "0.1.0" authors = ["Nathan Vegdahl "] edition = "2018" -license = "MIT" +license = "MIT, Apache 2.0" [lib] -name = "oct32norm" +name = "compact" path = "src/lib.rs" [dev-dependencies] diff --git a/sub_crates/oct32norm/LICENSE.md b/sub_crates/compact/LICENSE.md similarity index 100% rename from sub_crates/oct32norm/LICENSE.md rename to sub_crates/compact/LICENSE.md diff --git a/sub_crates/trifloat/benches/bench.rs b/sub_crates/compact/benches/bench.rs similarity index 74% rename from sub_crates/trifloat/benches/bench.rs rename to sub_crates/compact/benches/bench.rs index 1e7002b..29ee73c 100644 --- a/sub_crates/trifloat/benches/bench.rs +++ b/sub_crates/compact/benches/bench.rs @@ -1,6 +1,10 @@ use bencher::{benchmark_group, benchmark_main, black_box, Bencher}; +use compact::{ + fluv::fluv32, + shared_exp::{signed48, unsigned32, unsigned40}, + unit_vec::oct32, +}; use rand::{rngs::SmallRng, FromEntropy, Rng}; -use trifloat::{fluv32, signed48, unsigned32, unsigned40}; //---- @@ -115,6 +119,40 @@ fn fluv32_decode_yuv_1000_values(bench: &mut Bencher) { }); } +fn oct32_encode_1000_values(bench: &mut Bencher) { + let mut rng = SmallRng::from_entropy(); + bench.iter(|| { + let x = rng.gen::() - 0.5; + let y = rng.gen::() - 0.5; + let z = rng.gen::() - 0.5; + for _ in 0..1000 { + black_box(oct32::encode(black_box((x, y, z)))); + } + }); +} + +fn oct32_encode_precise_1000_values(bench: &mut Bencher) { + let mut rng = SmallRng::from_entropy(); + bench.iter(|| { + let x = rng.gen::() - 0.5; + let y = rng.gen::() - 0.5; + let z = rng.gen::() - 0.5; + for _ in 0..1000 { + black_box(oct32::encode_precise(black_box((x, y, z)))); + } + }); +} + +fn oct32_decode_1000_values(bench: &mut Bencher) { + let mut rng = SmallRng::from_entropy(); + bench.iter(|| { + let v = rng.gen::(); + for _ in 0..1000 { + black_box(oct32::decode(black_box(v))); + } + }); +} + //---- benchmark_group!( @@ -128,5 +166,8 @@ benchmark_group!( fluv32_encode_1000_values, fluv32_decode_1000_values, fluv32_decode_yuv_1000_values, + oct32_encode_1000_values, + oct32_encode_precise_1000_values, + oct32_decode_1000_values, ); benchmark_main!(benches); diff --git a/sub_crates/trifloat/src/fluv32.rs b/sub_crates/compact/src/fluv/fluv32.rs similarity index 83% rename from sub_crates/trifloat/src/fluv32.rs rename to sub_crates/compact/src/fluv/fluv32.rs index 09fc1d1..3017ae3 100644 --- a/sub_crates/trifloat/src/fluv32.rs +++ b/sub_crates/compact/src/fluv/fluv32.rs @@ -8,13 +8,12 @@ //! tweaked scales to allow perfect representation of E. //! * It uses a floating point rather than log encoding to store luminance, //! mainly for the sake of faster decoding. -//! * Unlike LogLuv, this format's dynamic range is biased to put more of it -//! above 1.0 (see Luminance details below). //! * It omits the sign bit of LogLuv, foregoing negative luminance //! capabilities. //! -//! This format has the same chroma precision, very slightly improved luminance -//! precision, and the same 127-stops of dynamic range as LogLuv. +//! Aside from that, this format has the same chroma precision, very slightly +//! improved luminance precision, and the same 127-stops of dynamic range as +//! LogLuv. //! //! Like the LogLuv format, this is an absolute rather than relative color //! encoding, and as such takes CIE XYZ triplets as input. It is *not* @@ -23,47 +22,38 @@ //! //! The bit layout is (from most to least significant bit): //! -//! * 7 bits: luminance exponent (bias 42) +//! * 7 bits: luminance exponent (bias 63) //! * 9 bits: luminance mantissa (implied leading 1, for 10 bits precision) //! * 8 bits: u' //! * 8 bits: v' //! //! ## Luminance details //! +//! Like typical floating point, the luminance mantissa is treated as having an +//! implicit leading 1, giving it an extra bit of precision. +//! +//! The luminance exponent is stored in 7 bits with a bias of 63. The smallest +//! exponent indicates a value of zero, and a valid encoding should also set +//! the mantissa to zero in that case (denormal numbers are not supported). +//! The largest exponent is given no special treatment (no infinities, no NaN). +//! +//! All together, this gives Fluv32 a worst-case precision that's slightly +//! better than Logluv, and a luminance range of roughly `10^-19` to `10^19`, +//! essentially the same as Logluv. +//! //! Quoting Greg Ward about luminance ranges: //! //! > The sun is about `10^8 cd/m^2`, and the underside of a rock on a moonless //! > night is probably around `10^-6` or so [...] //! -//! See also Wikipedia's -//! [list of luminance levels](https://en.wikipedia.org/wiki/Orders_of_magnitude_(luminance)). -//! -//! The luminance range of the original LogLuv is about `10^-19` to `10^19`, -//! splitting the range evenly above and below 1.0. Given the massive dynamic -//! range, and the fact that all day-to-day luminance levels trivially fit -//! within that, that's a perfectly reasonable choice. -//! -//! However, there are some stellar events like supernovae that are trillions -//! of times brighter than the sun, and would exceed `10^19`. Conversely, -//! there likely isn't much use for significantly smaller values than `10^-10` -//! or so. So although recording supernovae in physical units with a graphics -//! format seems unlikely, it doesn't hurt to bias the range towards brighter -//! luminance levels. -//! -//! With that in mind, FLuv32 uses an exponent bias of 42, putting twice as -//! many stops of dynamic range above 1.0 as below it, giving a luminance range -//! of roughly `10^-13` to `10^25`. It's the same dynamic range as -//! LogLuv (about 127 stops), but with more of that range placed above 1.0. -//! -//! Like typical floating point, the mantissa is treated as having an implicit -//! leading 1, giving it an extra bit of precision. The smallest exponent -//! indicates a value of zero, and a valid encoding should also set the -//! mantissa to zero in that case (denormal numbers are not supported). The -//! largest exponent is given no special treatment (no infinities, no NaN). +//! So Fluv32's luminance range is *massively* larger than needed for any +//! day-to-day phenomena. The only things that exceed it are stellar events +//! such as supernovae, images of which are unliklely to be used with physical +//! units in most practical graphics applications. #![allow(clippy::cast_lossless)] -const EXP_BIAS: i32 = 42; +const EXP_BIAS: i32 = 63; const BIAS_OFFSET: u32 = 127 - EXP_BIAS as u32; /// The scale factor of the quantized U component. @@ -205,10 +195,10 @@ mod tests { let tri = encode(fs); let fs2 = decode(tri); - assert_eq!(fs.1, fs2.1); assert!((fs.0 - fs2.0).abs() < 0.0000001); + assert_eq!(fs.1, fs2.1); assert!((fs.2 - fs2.2).abs() < 0.0000001); - assert_eq!(0x540056c3, tri); + assert_eq!(0x7e0056c3, tri); } #[test] diff --git a/sub_crates/compact/src/fluv/mod.rs b/sub_crates/compact/src/fluv/mod.rs new file mode 100644 index 0000000..c0ff7af --- /dev/null +++ b/sub_crates/compact/src/fluv/mod.rs @@ -0,0 +1,5 @@ +//! Fluv, a set of formats for compactly storing HDR XYZ colors. +//! +//! At the moment, only a 32-bit variant of the fluv format is provided. + +pub mod fluv32; diff --git a/sub_crates/compact/src/lib.rs b/sub_crates/compact/src/lib.rs new file mode 100644 index 0000000..1b1a3e8 --- /dev/null +++ b/sub_crates/compact/src/lib.rs @@ -0,0 +1,8 @@ +//! Functions for storing various kinds of data compactly, using domain +//! knowledge of how that data is used. +//! +//! This includes functions for compactly storing e.g. colors and unit vectors. + +pub mod fluv; +pub mod shared_exp; +pub mod unit_vec; diff --git a/sub_crates/trifloat/src/lib.rs b/sub_crates/compact/src/shared_exp/mod.rs similarity index 74% rename from sub_crates/trifloat/src/lib.rs rename to sub_crates/compact/src/shared_exp/mod.rs index ba1350d..1d7ac26 100644 --- a/sub_crates/trifloat/src/lib.rs +++ b/sub_crates/compact/src/shared_exp/mod.rs @@ -1,16 +1,11 @@ -//! Functions for storing triplets of floating point values in a -//! shared-exponent format. -//! -//! The motivating use-case for this is compactly storing HDR RGB colors. But -//! it may be useful for other things as well. +//! Shared-exponent float triplet formats. -pub mod fluv32; pub mod signed48; pub mod unsigned32; pub mod unsigned40; //=========================================================================== -// Some shared functions used by the other modules in this crate. +// Shared functions used by the other modules in this crate. /// Calculates 2.0^exp using IEEE bit fiddling. /// diff --git a/sub_crates/trifloat/src/signed48.rs b/sub_crates/compact/src/shared_exp/signed48.rs similarity index 99% rename from sub_crates/trifloat/src/signed48.rs rename to sub_crates/compact/src/shared_exp/signed48.rs index f43274d..c4fb517 100644 --- a/sub_crates/trifloat/src/signed48.rs +++ b/sub_crates/compact/src/shared_exp/signed48.rs @@ -15,7 +15,7 @@ #![allow(clippy::cast_lossless)] -use crate::{fiddle_exp2, fiddle_log2}; +use super::{fiddle_exp2, fiddle_log2}; /// Largest representable number. pub const MAX: f32 = ((1u128 << (64 - EXP_BIAS)) - (1 << (64 - EXP_BIAS - 13))) as f32; diff --git a/sub_crates/trifloat/src/unsigned32.rs b/sub_crates/compact/src/shared_exp/unsigned32.rs similarity index 99% rename from sub_crates/trifloat/src/unsigned32.rs rename to sub_crates/compact/src/shared_exp/unsigned32.rs index 700e45a..c26c4ea 100644 --- a/sub_crates/trifloat/src/unsigned32.rs +++ b/sub_crates/compact/src/shared_exp/unsigned32.rs @@ -11,7 +11,7 @@ //! of all three values depends on the largest of the three. All integers //! up to 512 can be represented exactly in the largest value. -use crate::{fiddle_exp2, fiddle_log2}; +use super::{fiddle_exp2, fiddle_log2}; /// Largest representable number. pub const MAX: f32 = ((1u64 << (32 - EXP_BIAS)) - (1 << (32 - EXP_BIAS - 9))) as f32; diff --git a/sub_crates/trifloat/src/unsigned40.rs b/sub_crates/compact/src/shared_exp/unsigned40.rs similarity index 99% rename from sub_crates/trifloat/src/unsigned40.rs rename to sub_crates/compact/src/shared_exp/unsigned40.rs index 3766d41..39cab7f 100644 --- a/sub_crates/trifloat/src/unsigned40.rs +++ b/sub_crates/compact/src/shared_exp/unsigned40.rs @@ -11,7 +11,7 @@ //! of all three values depends on the largest of the three. All integers //! up to 2048 can be represented exactly in the largest value. -use crate::{fiddle_exp2, fiddle_log2}; +use super::{fiddle_exp2, fiddle_log2}; /// Largest representable number. pub const MAX: f32 = ((1u128 << (128 - EXP_BIAS)) - (1 << (128 - EXP_BIAS - 11))) as f32; diff --git a/sub_crates/compact/src/unit_vec/mod.rs b/sub_crates/compact/src/unit_vec/mod.rs new file mode 100644 index 0000000..1f3648f --- /dev/null +++ b/sub_crates/compact/src/unit_vec/mod.rs @@ -0,0 +1,3 @@ +//! 3d unit vector formats. + +pub mod oct32; diff --git a/sub_crates/oct32norm/src/lib.rs b/sub_crates/compact/src/unit_vec/oct32.rs similarity index 100% rename from sub_crates/oct32norm/src/lib.rs rename to sub_crates/compact/src/unit_vec/oct32.rs diff --git a/sub_crates/oct32norm/tests/proptest_tests.rs b/sub_crates/compact/tests/proptest_tests.rs similarity index 89% rename from sub_crates/oct32norm/tests/proptest_tests.rs rename to sub_crates/compact/tests/proptest_tests.rs index 3234055..16d4336 100644 --- a/sub_crates/oct32norm/tests/proptest_tests.rs +++ b/sub_crates/compact/tests/proptest_tests.rs @@ -1,8 +1,7 @@ #[macro_use] extern crate proptest; -extern crate oct32norm; -use oct32norm::{decode, encode, encode_precise}; +use compact::unit_vec::oct32::{decode, encode, encode_precise}; use proptest::test_runner::Config; /// Calculates the cosine of the angle between the two vectors, @@ -54,7 +53,7 @@ proptest! { #![proptest_config(Config::with_cases(4096))] #[test] - fn pt_roundtrip_angle_precision(v in (-1.0f32..1.0, -1.0f32..1.0, -1.0f32..1.0)) { + fn oct32_pt_roundtrip_angle_precision(v in (-1.0f32..1.0, -1.0f32..1.0, -1.0f32..1.0)) { let oct = encode(v); let octp = encode_precise(v); @@ -68,7 +67,7 @@ proptest! { } #[test] - fn pt_roundtrip_component_precision(v in (-1.0f32..1.0, -1.0f32..1.0, -1.0f32..1.0)) { + fn oct32_pt_roundtrip_component_precision(v in (-1.0f32..1.0, -1.0f32..1.0, -1.0f32..1.0)) { let oct = encode(v); let octp = encode_precise(v); diff --git a/sub_crates/math3d/Cargo.toml b/sub_crates/math3d/Cargo.toml index 792c5b5..0f69425 100644 --- a/sub_crates/math3d/Cargo.toml +++ b/sub_crates/math3d/Cargo.toml @@ -3,7 +3,7 @@ name = "math3d" version = "0.1.0" authors = ["Nathan Vegdahl "] edition = "2018" -license = "MIT" +license = "MIT, Apache 2.0" [lib] name = "math3d" diff --git a/sub_crates/oct32norm/benches/bench.rs b/sub_crates/oct32norm/benches/bench.rs deleted file mode 100644 index 012d7fb..0000000 --- a/sub_crates/oct32norm/benches/bench.rs +++ /dev/null @@ -1,49 +0,0 @@ -use bencher::{benchmark_group, benchmark_main, black_box, Bencher}; -use oct32norm::{decode, encode, encode_precise}; -use rand::{rngs::SmallRng, FromEntropy, Rng}; - -//---- - -fn encode_1000_values(bench: &mut Bencher) { - let mut rng = SmallRng::from_entropy(); - bench.iter(|| { - let x = rng.gen::() - 0.5; - let y = rng.gen::() - 0.5; - let z = rng.gen::() - 0.5; - for _ in 0..1000 { - black_box(encode(black_box((x, y, z)))); - } - }); -} - -fn encode_precise_1000_values(bench: &mut Bencher) { - let mut rng = SmallRng::from_entropy(); - bench.iter(|| { - let x = rng.gen::() - 0.5; - let y = rng.gen::() - 0.5; - let z = rng.gen::() - 0.5; - for _ in 0..1000 { - black_box(encode_precise(black_box((x, y, z)))); - } - }); -} - -fn decode_1000_values(bench: &mut Bencher) { - let mut rng = SmallRng::from_entropy(); - bench.iter(|| { - let v = rng.gen::(); - for _ in 0..1000 { - black_box(decode(black_box(v))); - } - }); -} - -//---- - -benchmark_group!( - benches, - encode_1000_values, - encode_precise_1000_values, - decode_1000_values, -); -benchmark_main!(benches); diff --git a/sub_crates/sobol/Cargo.toml b/sub_crates/sobol/Cargo.toml index bca602a..a60eb43 100644 --- a/sub_crates/sobol/Cargo.toml +++ b/sub_crates/sobol/Cargo.toml @@ -3,7 +3,7 @@ name = "sobol" version = "0.1.0" authors = ["Nathan Vegdahl "] edition = "2018" -license = "MIT" +license = "MIT, Apache 2.0" build = "build.rs" [lib] diff --git a/sub_crates/spectral_upsampling/Cargo.toml b/sub_crates/spectral_upsampling/Cargo.toml index a9bf965..7d22204 100644 --- a/sub_crates/spectral_upsampling/Cargo.toml +++ b/sub_crates/spectral_upsampling/Cargo.toml @@ -3,7 +3,7 @@ name = "spectral_upsampling" version = "0.1.0" authors = ["Nathan Vegdahl "] edition = "2018" -license = "MIT" +license = "MIT, Apache 2.0" [lib] name = "spectral_upsampling" diff --git a/sub_crates/trifloat/Cargo.toml b/sub_crates/trifloat/Cargo.toml deleted file mode 100644 index 05b8f01..0000000 --- a/sub_crates/trifloat/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "trifloat" -version = "0.1.0" -authors = ["Nathan Vegdahl "] -edition = "2018" -license = "MIT" - -[lib] -name = "trifloat" -path = "src/lib.rs" - -[dev-dependencies] -bencher = "0.1.5" -rand = "0.6" - -[[bench]] -name = "bench" -harness = false \ No newline at end of file diff --git a/sub_crates/trifloat/LICENSE.md b/sub_crates/trifloat/LICENSE.md deleted file mode 100644 index c02a42a..0000000 --- a/sub_crates/trifloat/LICENSE.md +++ /dev/null @@ -1,8 +0,0 @@ -Copyright (c) 2020 Nathan Vegdahl - -This project is licensed under either of - -* MIT license (licenses/MIT.txt or http://opensource.org/licenses/MIT) -* Apache License, Version 2.0, (licenses/Apache-2.0.txt or http://www.apache.org/licenses/LICENSE-2.0) - -at your option.