Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ multiple_inherent_impl = "allow"
upper_case_acronyms = "allow"
struct_field_names = "allow"
self_named_module_files = "allow"
mod_module_files = "allow"

# I just personally prefer the `match` syntax for if-let matching.
option_if_let_else = "allow"
Expand Down
5 changes: 2 additions & 3 deletions crates/total-viewsheds/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
name = "total-viewsheds"
version = "0.1.0"
edition = "2021"
rust-version = "1.88"
rust-version = "1.89"


[dependencies]
bytemuck = "1.22.0"
Expand All @@ -29,5 +30,3 @@ googletest = "0.14.2"

[lints]
workspace = true


99 changes: 67 additions & 32 deletions crates/total-viewsheds/src/compute.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
//! The main entrypoint for running computations.

use crate::los_pack::LineOfSightPacked;
use color_eyre::Result;
use rayon::iter::IntoParallelIterator as _;
use rayon::iter::ParallelIterator as _;
use rayon::ThreadPoolBuilder;
use std::sync::Mutex;
use std::time::Instant;

/// The number of angles we rotate through. The other half are done via "backwards" lines of sight.
pub const SECTOR_STEPS: u16 = 180;
Expand All @@ -27,10 +33,6 @@ pub struct Compute<'compute> {
pub longest_lines: Vec<crate::los_pack::LineOfSightPacked>,
}

/// `NUM_CORES` is the physical number of cores on a machine. Currently hardcoded to 8
/// as that is what an i9900k has, and is a common configuration.
/// TODO find a good syscall for this
const NUM_CORES: usize = 8;
/// Configuration for computing.
pub struct ComputeConfig {
/// The height of the observer that views viewsheds.
Expand Down Expand Up @@ -94,10 +96,6 @@ impl<'compute> Compute<'compute> {
// We only need the "chocolate box" section of rotations to do visibility calculations.
let rotations_size = kernel::chocolate_box::size(dem.width, dem.tvs_width);

#[expect(
clippy::if_then_some_else_none,
reason = "The `?` is hard to use in the closure"
)]
let vulkan = if matches!(config.backend, crate::config::Backend::Vulkan) {
let elevations = dem.elevations.clone();
dem.elevations = Vec::new(); // Free up some RAM.
Expand Down Expand Up @@ -259,32 +257,69 @@ impl<'compute> Compute<'compute> {

/// `run_parallel` runs the CPU kernel in parallel
fn run_parallel(&mut self) -> Result<()> {
#[expect(
clippy::as_conversions,
clippy::cast_possible_truncation,
reason = "elevations start out as i16s, and i16 -> f32 -> i16 is lossless"
)]
let elevations = self
.dem
.elevations
.iter()
.map(|&x| x as i16)
.collect::<Vec<i16>>();

#[expect(clippy::as_conversions, reason = "u32 -> usize is valid")]
// TODO: third param is ring data which needs to be saved
let (surfaces, _longest, _) = crate::cpu::multithreaded_kernel(
&elevations,
self.dem.max_los_as_points as usize,
360,
NUM_CORES,
false,
);
let max_los = usize::try_from(self.dem.max_los_as_points)?;
let mut surfaces = vec![0.0f32; max_los * max_los];
let mut longest = vec![(0u16, 0.0f32); max_los * max_los];

let pool = ThreadPoolBuilder::new().num_threads(8).build()?;

self.add_sector_surfaces_to_running_total(&surfaces);
{
let angle_mu = &Mutex::new(&mut surfaces);
let longest_mu = &Mutex::new(&mut longest);

let elevations = &self.dem.elevations;

pool.install(move || {
(0u16..360u16)
.into_par_iter()
.map(|angle| {
let start = Instant::now();
tracing::info!("starting angle: {angle}");
let (heatmap, long, _) =
crate::cpu::kernel(elevations, max_los, f32::from(angle), false);
tracing::info!("finished angle in {:?}", start.elapsed());
(angle, heatmap, long)
})
.for_each(|(angle, heatmap, long)| {
#[expect(clippy::expect_used, reason = "a poisoned mutex should crash")]
angle_mu
.lock()
.expect("mutex poisoned")
.iter_mut()
.zip(heatmap)
.for_each(|(to, from)| {
*to += from;
});

#[expect(clippy::expect_used, reason = "a poisoned mutex should crash")]
longest_mu
.lock()
.expect("mutex poisoned")
.iter_mut()
.zip(long)
.for_each(|(to, from)| {
if from > to.1 {
*to = (angle, from);
}
});
});
});
};

// TODO: Pack longest lines
// self.longest_lines = longest;
self.total_surfaces = surfaces;
let packed: Result<Vec<LineOfSightPacked>> = longest
.iter()
.map(|&(angle, distance): &(u16, f32)| {
#[expect(
clippy::as_conversions,
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
reason = "distances always fit in u32"
)]
LineOfSightPacked::new(distance as u32, angle)
})
.collect();
self.longest_lines = packed?;

self.render_total_surfaces()?;
self.render_longest_lines()?;
Expand Down
Loading