Merge branch 'master' into micropoly

This commit is contained in:
Nathan Vegdahl 2020-12-30 20:44:18 +09:00
commit 72d9eccf99
24 changed files with 113 additions and 159 deletions

29
Cargo.lock generated
View File

@ -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"
@ -281,15 +290,6 @@ dependencies = [
"libc",
]
[[package]]
name = "oct32norm"
version = "0.1.0"
dependencies = [
"bencher",
"proptest",
"rand 0.6.5",
]
[[package]]
name = "openexr"
version = "0.6.0"
@ -356,6 +356,7 @@ dependencies = [
"bvh_order",
"clap",
"color",
"compact",
"copy_in_place",
"crossbeam",
"data_tree",
@ -368,7 +369,6 @@ dependencies = [
"math3d",
"nom",
"num_cpus",
"oct32norm",
"openexr",
"png_encode_mini",
"rustc-serialize",
@ -376,7 +376,6 @@ dependencies = [
"sobol",
"spectral_upsampling",
"time",
"trifloat",
]
[[package]]
@ -668,14 +667,6 @@ dependencies = [
"winapi",
]
[[package]]
name = "trifloat"
version = "0.1.0"
dependencies = [
"bencher",
"rand 0.6.5",
]
[[package]]
name = "unicode-width"
version = "0.1.8"

View File

@ -2,13 +2,12 @@
members = [
"sub_crates/bvh_order",
"sub_crates/color",
"sub_crates/compact",
"sub_crates/data_tree",
"sub_crates/halton",
"sub_crates/math3d",
"sub_crates/oct32norm",
"sub_crates/sobol",
"sub_crates/spectral_upsampling",
"sub_crates/trifloat"
]
[package]
@ -16,6 +15,7 @@ name = "psychopath"
version = "0.1.0"
authors = ["Nathan Vegdahl <cessen@cessen.com>"]
edition = "2018"
license = "GPL v3"
[profile.release]
debug = true
@ -46,6 +46,9 @@ path = "sub_crates/bvh_order"
[dependencies.color]
path = "sub_crates/color"
[dependencies.compact]
path = "sub_crates/compact"
[dependencies.data_tree]
path = "sub_crates/data_tree"
@ -55,14 +58,8 @@ 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"

View File

@ -2,10 +2,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};

View File

@ -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]

View File

@ -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]

View File

@ -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]

View File

@ -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);

View File

@ -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]

View 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;

View 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;

View File

@ -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.
///

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -0,0 +1,3 @@
//! 3d unit vector formats.
pub mod oct32;

View File

@ -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);

View File

@ -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"

View File

@ -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);

View File

@ -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]

View File

@ -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"

View File

@ -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

View File

@ -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.