Parallelize lighting filters

parent 3098964e
......@@ -32,6 +32,14 @@ dependencies = [
"num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "arrayvec"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "atty"
version = "0.2.10"
......@@ -196,6 +204,37 @@ dependencies = [
"thread-scoped 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "crossbeam-deque"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "crossbeam-epoch"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
"scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "crossbeam-utils"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cssparser"
version = "0.24.0"
......@@ -422,6 +461,11 @@ dependencies = [
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "memoffset"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "nalgebra"
version = "0.16.0"
......@@ -437,6 +481,11 @@ dependencies = [
"typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "nodrop"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "num-complex"
version = "0.2.0"
......@@ -654,6 +703,27 @@ name = "rawpointer"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rayon"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rayon-core 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rayon-core"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "redox_syscall"
version = "0.1.40"
......@@ -711,6 +781,7 @@ dependencies = [
"pangocairo-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"phf 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)",
"phf_codegen 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)",
"rayon 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
......@@ -719,6 +790,11 @@ name = "rustc-demangle"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "scopeguard"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde"
version = "1.0.70"
......@@ -937,6 +1013,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum alga 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d3d5dbb7fa6898343fd31e14e8abf2563b6c61a81c1a2e15a678a2f351a249ed"
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
"checksum approx 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f71f10b5c4946a64aad7b8cf65e3406cd3da22fc448595991d22423cf6db67b4"
"checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef"
"checksum atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "2fc4a1aa4c24c0718a250f0681885c1af91419d242f29eb8f2ab28502d80dbd1"
"checksum backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "89a47830402e9981c5c41223151efcced65a0510c13097c769cede7efb34782a"
"checksum backtrace-sys 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)" = "bff67d0c06556c0b8e6b5f090f0eac52d950d9dfd1d35ba04e4ca3543eaf6a7e"
......@@ -955,6 +1032,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum criterion 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b027da737a814e7ffcfdbaf1c0509fbaea4c6df5f2e116c820ecf515ad39ac9b"
"checksum criterion-plot 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7894b77aa2820337ddf3a6655c24116fb7e53dbdc9735d43a4dec7da5d2c4716"
"checksum criterion-stats 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "61ee46663c1c4c770b215f6c412fb1e822cfb5122dbc98347dc5789613c69d2b"
"checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3"
"checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150"
"checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9"
"checksum cssparser 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)" = "495beddc39b1987b8e9f029354eccbd5ef88eb5f1cd24badb764dce338acf2e0"
"checksum cssparser-macros 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "72b64d0d64184a082f38028396875c08c8ee083481f6f16e6e0cf52c338bd785"
"checksum csv 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71903184af9960c555e7f3b32ff17390d20ecaaf17d4f18c4a0993f2df8a49e3"
......@@ -982,7 +1062,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "100aabe6b8ff4e4a7e32c1c13523379802df0772b82466207ac25b013f193376"
"checksum matrixmultiply 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "cac1a66eab356036af85ea093101a14223dc6e3f4c02a59b7d572e5b93270bf7"
"checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d"
"checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3"
"checksum nalgebra 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4534bc88a178e411e634e1e44e7f2ad6e9bf062ea539f40e579bd49ef2fad056"
"checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2"
"checksum num-complex 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "68de83578789e0fbda3fa923035be83cf8bfd3b30ccfdecd5aa89bf8601f408e"
"checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea"
"checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31"
......@@ -1009,11 +1091,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum rand 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "12397506224b2f93e6664ffc4f664b29be8208e5157d3d90b44f09b5fae470ea"
"checksum rand_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "edecf0f94da5551fc9b492093e30b041a891657db7940ee221f9d2f66e82eef2"
"checksum rawpointer 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ebac11a9d2e11f2af219b8b8d833b76b1ea0e054aa0e8d8e9e4cbde353bdf019"
"checksum rayon 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "df7a791f788cb4c516f0e091301a29c2b71ef680db5e644a7d68835c8ae6dbfa"
"checksum rayon-core 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b055d1e92aba6877574d8fe604a63c8b5df60f60e5982bf7ccbb1338ea527356"
"checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1"
"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
"checksum regex 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "13c93d55961981ba9226a213b385216f83ab43bd6ac53ab16b2eeb47e337cf4e"
"checksum regex-syntax 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05b06a75f5217880fc5e905952a42750bf44787e56a6c6d6852ed0992f5e1d54"
"checksum rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "bcfe5b13211b4d78e5c2cadfebd7769197d95c639c35a50057eb4c05de811395"
"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27"
"checksum serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)" = "0c3adf19c07af6d186d91dae8927b83b0553d07ca56cbf7f2f32560455c91920"
"checksum serde_derive 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)" = "3525a779832b08693031b8ecfb0de81cd71cfd3812088fafe9a7496789572124"
"checksum serde_json 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)" = "c3c6908c7b925cd6c590358a4034de93dbddb20c45e1d021931459fd419bf0e2"
......
......@@ -35,6 +35,7 @@ float-cmp = "0.4.0"
nalgebra = "0.16"
num-traits = "0.2"
owning_ref = "0.3"
rayon = "1"
[dependencies.cairo-sys-rs]
version = "0.6"
......
......@@ -5,6 +5,7 @@ use cairo::{self, ImageSurface, MatrixTrait};
use cssparser;
use nalgebra::Vector3;
use num_traits::identities::Zero;
use rayon::prelude::*;
use attributes::Attribute;
use drawing_ctx::DrawingCtx;
......@@ -51,6 +52,18 @@ enum Data {
},
}
/// `Data` but without `Cell`s, needed for sharing between threads.
#[derive(Clone, Copy)]
enum RawData {
Diffuse {
diffuse_constant: f64,
},
Specular {
specular_constant: f64,
specular_exponent: f64,
},
}
/// The `feDiffuseLighting` and `feSpecularLighting` filter primitives.
pub struct Lighting {
base: PrimitiveWithInput,
......@@ -253,87 +266,91 @@ impl Filter for Lighting {
let output_stride = output_surface.get_stride() as usize;
{
let mut output_data = output_surface.get_data().unwrap();
let output_slice = &mut *output_data;
let data = self.data.to_raw();
let mut compute_output_pixel = |x, y, normal: Normal| {
let pixel = input_surface.get_pixel(x, y);
let scaled_x = f64::from(x) * ox;
let scaled_y = f64::from(y) * oy;
let z = f64::from(pixel.a) / 255.0 * surface_scale;
let light_vector = light_source.vector(scaled_x, scaled_y, z);
let light_color = light_source.color(lighting_color, light_vector);
let factor = match self.data {
Data::Diffuse {
ref diffuse_constant,
} => {
let k = if normal.normal.is_zero() {
// Common case of (0, 0, 1) normal.
light_vector.z
} else {
let mut n = normal.normal.map(|x| f64::from(x) * surface_scale / 255.);
n.component_mul_assign(&normal.factor);
let normal = Vector3::new(n.x, n.y, 1.0);
normal.dot(&light_vector) / normal.norm()
};
diffuse_constant.get() * k
}
Data::Specular {
ref specular_constant,
ref specular_exponent,
} => {
let h = light_vector + Vector3::new(0.0, 0.0, 1.0);
let h_norm = h.norm();
if h_norm == 0.0 {
0.0
} else {
let compute_output_pixel =
|mut output_slice: &mut [u8], base_y, x, y, normal: Normal| {
let pixel = input_surface.get_pixel(x, y);
let scaled_x = f64::from(x) * ox;
let scaled_y = f64::from(y) * oy;
let z = f64::from(pixel.a) / 255.0 * surface_scale;
let light_vector = light_source.vector(scaled_x, scaled_y, z);
let light_color = light_source.color(lighting_color, light_vector);
let factor = match data {
RawData::Diffuse { diffuse_constant } => {
let k = if normal.normal.is_zero() {
// Common case of (0, 0, 1) normal.
let n_dot_h = h.z / h_norm;
if specular_exponent.get() == 1.0 {
n_dot_h
} else {
n_dot_h.powf(specular_exponent.get())
}
light_vector.z
} else {
let mut n =
normal.normal.map(|x| f64::from(x) * surface_scale / 255.);
n.component_mul_assign(&normal.factor);
let normal = Vector3::new(n.x, n.y, 1.0);
let n_dot_h = normal.dot(&h) / normal.norm() / h_norm;
if specular_exponent.get() == 1.0 {
n_dot_h
} else {
n_dot_h.powf(specular_exponent.get())
}
normal.dot(&light_vector) / normal.norm()
};
specular_constant.get() * k
diffuse_constant * k
}
}
};
RawData::Specular {
specular_constant,
specular_exponent,
} => {
let h = light_vector + Vector3::new(0.0, 0.0, 1.0);
let h_norm = h.norm();
if h_norm == 0.0 {
0.0
} else {
let k = if normal.normal.is_zero() {
// Common case of (0, 0, 1) normal.
let n_dot_h = h.z / h_norm;
if specular_exponent == 1.0 {
n_dot_h
} else {
n_dot_h.powf(specular_exponent)
}
} else {
let mut n =
normal.normal.map(|x| f64::from(x) * surface_scale / 255.);
n.component_mul_assign(&normal.factor);
let normal = Vector3::new(n.x, n.y, 1.0);
let n_dot_h = normal.dot(&h) / normal.norm() / h_norm;
if specular_exponent == 1.0 {
n_dot_h
} else {
n_dot_h.powf(specular_exponent)
}
};
let compute = |x| (clamp(factor * f64::from(x), 0.0, 255.0) + 0.5) as u8;
specular_constant * k
}
}
};
let mut output_pixel = Pixel {
r: compute(light_color.red),
g: compute(light_color.green),
b: compute(light_color.blue),
a: 255,
};
let compute = |x| (clamp(factor * f64::from(x), 0.0, 255.0) + 0.5) as u8;
if let Data::Specular { .. } = self.data {
output_pixel.a = max(max(output_pixel.r, output_pixel.g), output_pixel.b);
}
let mut output_pixel = Pixel {
r: compute(light_color.red),
g: compute(light_color.green),
b: compute(light_color.blue),
a: 255,
};
output_data.set_pixel(output_stride, output_pixel, x, y);
};
if let RawData::Specular { .. } = data {
output_pixel.a = max(max(output_pixel.r, output_pixel.g), output_pixel.b);
}
output_slice.set_pixel(output_stride, output_pixel, x, y - base_y);
};
// Top left.
compute_output_pixel(
output_slice,
0,
bounds.x0 as u32,
bounds.y0 as u32,
top_left_normal(&input_surface, bounds),
......@@ -341,6 +358,8 @@ impl Filter for Lighting {
// Top right.
compute_output_pixel(
output_slice,
0,
bounds.x1 as u32 - 1,
bounds.y0 as u32,
top_right_normal(&input_surface, bounds),
......@@ -348,6 +367,8 @@ impl Filter for Lighting {
// Bottom left.
compute_output_pixel(
output_slice,
0,
bounds.x0 as u32,
bounds.y1 as u32 - 1,
bottom_left_normal(&input_surface, bounds),
......@@ -355,6 +376,8 @@ impl Filter for Lighting {
// Bottom right.
compute_output_pixel(
output_slice,
0,
bounds.x1 as u32 - 1,
bounds.y1 as u32 - 1,
bottom_right_normal(&input_surface, bounds),
......@@ -364,6 +387,8 @@ impl Filter for Lighting {
// Top row.
for x in bounds.x0 as u32 + 1..bounds.x1 as u32 - 1 {
compute_output_pixel(
output_slice,
0,
x,
bounds.y0 as u32,
top_row_normal(&input_surface, bounds, x),
......@@ -373,6 +398,8 @@ impl Filter for Lighting {
// Bottom row.
for x in bounds.x0 as u32 + 1..bounds.x1 as u32 - 1 {
compute_output_pixel(
output_slice,
0,
x,
bounds.y1 as u32 - 1,
bottom_row_normal(&input_surface, bounds, x),
......@@ -384,6 +411,8 @@ impl Filter for Lighting {
// Left column.
for y in bounds.y0 as u32 + 1..bounds.y1 as u32 - 1 {
compute_output_pixel(
output_slice,
0,
bounds.x0 as u32,
y,
left_column_normal(&input_surface, bounds, y),
......@@ -393,6 +422,8 @@ impl Filter for Lighting {
// Right column.
for y in bounds.y0 as u32 + 1..bounds.y1 as u32 - 1 {
compute_output_pixel(
output_slice,
0,
bounds.x1 as u32 - 1,
y,
right_column_normal(&input_surface, bounds, y),
......@@ -402,11 +433,25 @@ impl Filter for Lighting {
if bounds.x1 - bounds.x0 >= 3 && bounds.y1 - bounds.y0 >= 3 {
// Interior pixels.
for y in bounds.y0 as u32 + 1..bounds.y1 as u32 - 1 {
for x in bounds.x0 as u32 + 1..bounds.x1 as u32 - 1 {
compute_output_pixel(x, y, interior_normal(&input_surface, bounds, x, y));
}
}
let first_row = bounds.y0 as u32 + 1;
let one_past_last_row = bounds.y1 as u32 - 1;
let first_pixel = (first_row as usize) * output_stride;
let one_past_last_pixel = (one_past_last_row as usize) * output_stride;
output_slice[first_pixel..one_past_last_pixel]
.par_chunks_mut(output_stride)
.zip(first_row..one_past_last_row)
.for_each(|(slice, y)| {
for x in bounds.x0 as u32 + 1..bounds.x1 as u32 - 1 {
compute_output_pixel(
slice,
y,
x,
y,
interior_normal(&input_surface, bounds, x, y),
);
}
});
}
}
......@@ -450,6 +495,24 @@ impl Filter for Lighting {
}
}
impl Data {
#[inline]
fn to_raw(&self) -> RawData {
match self {
Data::Diffuse { diffuse_constant } => RawData::Diffuse {
diffuse_constant: diffuse_constant.get(),
},
Data::Specular {
specular_constant,
specular_exponent,
} => RawData::Specular {
specular_constant: specular_constant.get(),
specular_exponent: specular_exponent.get(),
},
}
}
}
impl Default for Lighting {
#[inline]
fn default() -> Self {
......
......@@ -17,6 +17,7 @@ extern crate pango;
extern crate pango_cairo_sys;
extern crate pango_sys;
extern crate pangocairo;
extern crate rayon;
extern crate regex;
#[macro_use]
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment