Consolidate all of the compact value storage formats into one crate.
This commit is contained in:
parent
6586b38dc9
commit
3d85ce8005
29
Cargo.lock
generated
29
Cargo.lock
generated
|
@ -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"
|
||||
|
|
13
Cargo.toml
13
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 <cessen@cessen.com>"]
|
||||
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"
|
|
@ -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};
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ name = "bvh_order"
|
|||
version = "0.1.0"
|
||||
authors = ["Nathan Vegdahl <cessen@cessen.com>"]
|
||||
edition = "2018"
|
||||
license = "MIT"
|
||||
license = "MIT, Apache 2.0"
|
||||
build = "build.rs"
|
||||
|
||||
[lib]
|
||||
|
|
|
@ -3,7 +3,7 @@ name = "color"
|
|||
version = "0.1.0"
|
||||
authors = ["Nathan Vegdahl <cessen@cessen.com>"]
|
||||
edition = "2018"
|
||||
license = "MIT"
|
||||
license = "MIT, Apache 2.0"
|
||||
build = "build.rs"
|
||||
|
||||
[lib]
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
[package]
|
||||
name = "oct32norm"
|
||||
name = "compact"
|
||||
version = "0.1.0"
|
||||
authors = ["Nathan Vegdahl <cessen@cessen.com>"]
|
||||
edition = "2018"
|
||||
license = "MIT"
|
||||
license = "MIT, Apache 2.0"
|
||||
|
||||
[lib]
|
||||
name = "oct32norm"
|
||||
name = "compact"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dev-dependencies]
|
|
@ -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::<f32>() - 0.5;
|
||||
let y = rng.gen::<f32>() - 0.5;
|
||||
let z = rng.gen::<f32>() - 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::<f32>() - 0.5;
|
||||
let y = rng.gen::<f32>() - 0.5;
|
||||
let z = rng.gen::<f32>() - 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::<u32>();
|
||||
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);
|
|
@ -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]
|
5
sub_crates/compact/src/fluv/mod.rs
Normal file
5
sub_crates/compact/src/fluv/mod.rs
Normal file
|
@ -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;
|
8
sub_crates/compact/src/lib.rs
Normal file
8
sub_crates/compact/src/lib.rs
Normal file
|
@ -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;
|
|
@ -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.
|
||||
///
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
3
sub_crates/compact/src/unit_vec/mod.rs
Normal file
3
sub_crates/compact/src/unit_vec/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
//! 3d unit vector formats.
|
||||
|
||||
pub mod oct32;
|
|
@ -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);
|
||||
|
|
@ -3,7 +3,7 @@ name = "math3d"
|
|||
version = "0.1.0"
|
||||
authors = ["Nathan Vegdahl <cessen@cessen.com>"]
|
||||
edition = "2018"
|
||||
license = "MIT"
|
||||
license = "MIT, Apache 2.0"
|
||||
|
||||
[lib]
|
||||
name = "math3d"
|
||||
|
|
|
@ -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::<f32>() - 0.5;
|
||||
let y = rng.gen::<f32>() - 0.5;
|
||||
let z = rng.gen::<f32>() - 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::<f32>() - 0.5;
|
||||
let y = rng.gen::<f32>() - 0.5;
|
||||
let z = rng.gen::<f32>() - 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::<u32>();
|
||||
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);
|
|
@ -3,7 +3,7 @@ name = "sobol"
|
|||
version = "0.1.0"
|
||||
authors = ["Nathan Vegdahl <cessen@cessen.com>"]
|
||||
edition = "2018"
|
||||
license = "MIT"
|
||||
license = "MIT, Apache 2.0"
|
||||
build = "build.rs"
|
||||
|
||||
[lib]
|
||||
|
|
|
@ -3,7 +3,7 @@ name = "spectral_upsampling"
|
|||
version = "0.1.0"
|
||||
authors = ["Nathan Vegdahl <cessen@cessen.com>"]
|
||||
edition = "2018"
|
||||
license = "MIT"
|
||||
license = "MIT, Apache 2.0"
|
||||
|
||||
[lib]
|
||||
name = "spectral_upsampling"
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
[package]
|
||||
name = "trifloat"
|
||||
version = "0.1.0"
|
||||
authors = ["Nathan Vegdahl <cessen@cessen.com>"]
|
||||
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
|
|
@ -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.
|
Loading…
Reference in New Issue
Block a user