Cleanup slice operation stuff

This commit is contained in:
Connor Slade
2024-07-23 20:46:02 -04:00
parent 44be4a4d08
commit 869f4ef570
12 changed files with 202 additions and 60 deletions

76
Cargo.lock generated
View File

@@ -2420,6 +2420,12 @@ version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "lebe"
version = "0.5.2"
@@ -2652,9 +2658,12 @@ dependencies = [
"goo_format",
"image 0.25.1",
"nalgebra 0.32.6",
"parking_lot",
"plexus",
"rfd",
"slicer",
"tracing",
"tracing-subscriber",
"wgpu",
]
@@ -2811,6 +2820,16 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
[[package]]
name = "nu-ansi-term"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
dependencies = [
"overload",
"winapi",
]
[[package]]
name = "num"
version = "0.2.1"
@@ -3188,6 +3207,12 @@ dependencies = [
"pin-project-lite",
]
[[package]]
name = "overload"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "owned_ttf_parser"
version = "0.21.0"
@@ -3975,6 +4000,15 @@ dependencies = [
"digest",
]
[[package]]
name = "sharded-slab"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
dependencies = [
"lazy_static",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.2"
@@ -4272,6 +4306,16 @@ dependencies = [
"syn 2.0.66",
]
[[package]]
name = "thread_local"
version = "1.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
dependencies = [
"cfg-if",
"once_cell",
]
[[package]]
name = "tiff"
version = "0.9.1"
@@ -4418,6 +4462,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
dependencies = [
"once_cell",
"valuable",
]
[[package]]
name = "tracing-log"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
dependencies = [
"log",
"once_cell",
"tracing-core",
]
[[package]]
name = "tracing-subscriber"
version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
dependencies = [
"nu-ansi-term",
"sharded-slab",
"smallvec 1.13.2",
"thread_local",
"tracing-core",
"tracing-log",
]
[[package]]
@@ -4532,6 +4602,12 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "valuable"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "version-compare"
version = "0.2.0"

View File

@@ -17,8 +17,8 @@
- [ ] Anti-aliasing
- [x] Cache transformed points?
- [x] Rename `ui` module to `mslicer`
- [ ] Proper slice preview scaling
- [ ] Preview image generation
- [x] Proper slice preview scaling
- [x] Preview image generation
- [x] Don't clone mesh data when sending mesh to slicing thread
- [ ] Close details dropdown in models window when deleting mesh
- [x] Rotations in degrees
@@ -41,3 +41,6 @@
- [x] Optimize bvh more (benchmarking with criterion)
- [x] Add documentation to slicer
- [x] Make the slice preview layer slider full height
- [ ] Combine slice result and slice preview windows and add a close button
- [ ] Split slice operation to a new module
- [ ] Re-enable multi-sampling

View File

@@ -13,8 +13,11 @@ egui-wgpu = "0.27.2"
encase = { version = "0.8.0", features = ["nalgebra"] }
image = "0.25.1"
nalgebra = "0.32.6"
parking_lot = "0.12.3"
plexus = "0.0.11"
rfd = "0.14.1"
tracing = "0.1.40"
tracing-subscriber = "0.3.18"
wgpu = "0.19.4"
common = { path = "../common" }

View File

@@ -1,18 +1,16 @@
use std::{
sync::{Arc, Mutex, RwLock},
thread,
time::Instant,
};
use std::{sync::Arc, thread, time::Instant};
use clone_macro::clone;
use egui::{CentralPanel, Frame, Sense};
use egui_wgpu::Callback;
use image::{imageops::FilterType, RgbaImage};
use nalgebra::{Vector2, Vector3};
use parking_lot::{lock_api::MutexGuard, Condvar, MappedMutexGuard, Mutex, RawMutex, RwLock};
use slicer::{
slicer::{Progress as SliceProgress, Slicer},
Pos,
};
use tracing::info;
use crate::{
render::{
@@ -29,10 +27,7 @@ pub struct App {
pub slice_config: SliceConfig,
pub meshes: Arc<RwLock<Vec<RenderedMesh>>>,
// todo: clean this up
pub slice_progress: Option<SliceProgress>,
pub slice_result: Arc<Mutex<Option<SliceResult>>>,
pub slice_preview_image: Arc<Mutex<Option<RgbaImage>>>,
pub slice_operation: Option<SliceOperation>,
pub render_style: RenderStyle,
pub grid_size: f32,
@@ -40,6 +35,11 @@ pub struct App {
pub windows: Windows,
}
pub struct FpsTracker {
last_frame: Instant,
last_frame_time: f32,
}
pub struct SliceResult {
pub goo: GooFile,
@@ -49,14 +49,56 @@ pub struct SliceResult {
pub preview_scale: f32,
}
pub struct FpsTracker {
last_frame: Instant,
last_frame_time: f32,
// todo: Arc<SliceOperationInner>?
#[derive(Clone)]
pub struct SliceOperation {
pub progress: SliceProgress,
pub result: Arc<Mutex<Option<SliceResult>>>,
pub preview_image: Arc<Mutex<Option<RgbaImage>>>,
preview_condvar: Arc<Condvar>,
}
impl SliceOperation {
pub fn new(progress: SliceProgress) -> Self {
Self {
progress,
result: Arc::new(Mutex::new(None)),
preview_image: Arc::new(Mutex::new(None)),
preview_condvar: Arc::new(Condvar::new()),
}
}
pub fn needs_preview_image(&self) -> bool {
self.preview_image.lock().is_none()
}
pub fn add_preview_image(&self, image: RgbaImage) {
self.preview_image.lock().replace(image);
self.preview_condvar.notify_all();
}
pub fn preview_image(&self) -> MappedMutexGuard<'_, RgbaImage> {
let mut preview_image = self.preview_image.lock();
while preview_image.is_none() {
self.preview_condvar.wait(&mut preview_image);
}
MutexGuard::map(preview_image, |image| image.as_mut().unwrap())
}
pub fn add_result(&self, result: SliceResult) {
self.result.lock().replace(result);
}
pub fn result(&self) -> MutexGuard<RawMutex, Option<SliceResult>> {
self.result.lock()
}
}
impl App {
pub fn slice(&mut self) {
*self.slice_preview_image.lock().unwrap() = None;
info!("Starting slicing operation");
let slice_config = self.slice_config.clone();
let mut meshes = Vec::new();
@@ -68,7 +110,7 @@ impl App {
1.0,
);
for mesh in self.meshes.read().unwrap().iter().cloned() {
for mesh in self.meshes.read().iter().cloned() {
let mut mesh = mesh.mesh;
mesh.set_scale_unchecked(mesh.scale().component_mul(&mm_to_px));
@@ -94,25 +136,24 @@ impl App {
}
let slicer = Slicer::new(slice_config, meshes);
self.slice_progress = Some(slicer.progress());
self.slice_operation
.replace(SliceOperation::new(slicer.progress()));
thread::spawn(clone!(
[
{ self.slice_result } as slice_result,
{ self.slice_preview_image } as preview_image
],
[{ self.slice_operation } as slice_operation],
move || {
let slice_operation = slice_operation.as_ref().unwrap();
let mut goo = GooFile::from_slice_result(slicer.slice::<LayerEncoder>());
let preview_image = preview_image.lock().unwrap();
let preview_image = preview_image.as_ref().unwrap();
{
let preview_image = slice_operation.preview_image();
goo.header.big_preview =
PreviewImage::from_image_scaled(&preview_image, FilterType::Nearest);
goo.header.small_preview =
PreviewImage::from_image_scaled(&preview_image, FilterType::Nearest);
}
goo.header.big_preview =
PreviewImage::from_image_scaled(preview_image, FilterType::Nearest);
goo.header.small_preview =
PreviewImage::from_image_scaled(preview_image, FilterType::Nearest);
slice_result.lock().unwrap().replace(SliceResult {
slice_operation.add_result(SliceResult {
goo,
slice_preview_layer: 0,
last_preview_layer: 0,
@@ -149,9 +190,7 @@ impl eframe::App for App {
grid_size: self.grid_size,
is_moving: response.dragged(),
render_preview: (self.slice_progress.is_some()
&& self.slice_preview_image.lock().unwrap().is_none())
.then(|| self.slice_preview_image.clone()),
slice_operation: self.slice_operation.clone(),
models: self.meshes.clone(),
render_style: self.render_style,
@@ -208,9 +247,7 @@ impl Default for App {
render_style: RenderStyle::Normals,
grid_size: 12.16,
slice_progress: None,
slice_result: Arc::new(Mutex::new(None)),
slice_preview_image: Arc::new(Mutex::new(None)),
slice_operation: None,
}
}
}

View File

@@ -4,6 +4,8 @@ use anyhow::Result;
use eframe::NativeOptions;
use egui::{IconData, Vec2, ViewportBuilder};
use egui_wgpu::WgpuConfiguration;
use tracing::level_filters::LevelFilter;
use tracing_subscriber::{filter, layer::SubscriberExt, util::SubscriberInitExt};
use wgpu::{DeviceDescriptor, Features, TextureFormat};
const TEXTURE_FORMAT: TextureFormat = TextureFormat::Bgra8Unorm;
@@ -17,6 +19,11 @@ use app::App;
const ICON: &[u8] = include_bytes!("assets/icon.png");
fn main() -> Result<()> {
let filter = filter::Targets::new()
.with_default(LevelFilter::OFF)
.with_target("mslicer", LevelFilter::TRACE);
tracing_subscriber::registry().with(filter).init();
let icon = image::load_from_memory(ICON)?;
eframe::run_native(
"mslicer",

View File

@@ -102,7 +102,7 @@ impl Pipeline<WorkspaceRenderCallback> for ModelPipeline {
self.bind_groups.clear();
let mut to_generate = Vec::new();
for (idx, model) in resources.models.read().unwrap().iter().enumerate() {
for (idx, model) in resources.models.read().iter().enumerate() {
if model.try_get_buffers().is_none() {
to_generate.push(idx);
}
@@ -136,7 +136,7 @@ impl Pipeline<WorkspaceRenderCallback> for ModelPipeline {
}
if !to_generate.is_empty() {
let mut meshes = resources.models.write().unwrap();
let mut meshes = resources.models.write();
for idx in to_generate {
meshes[idx].get_buffers(device);
}
@@ -146,7 +146,7 @@ impl Pipeline<WorkspaceRenderCallback> for ModelPipeline {
fn paint<'a>(&'a self, render_pass: &mut RenderPass<'a>, resources: &WorkspaceRenderCallback) {
render_pass.set_pipeline(&self.render_pipeline);
let models = resources.models.read().unwrap();
let models = resources.models.read();
for (idx, model) in models.iter().enumerate().filter(|(_, x)| !x.hidden) {
render_pass.set_bind_group(0, &self.bind_groups[idx], &[]);

View File

@@ -3,6 +3,7 @@ use std::f32::consts::PI;
use egui_wgpu::ScreenDescriptor;
use image::{Rgba, RgbaImage};
use nalgebra::{Vector2, Vector3};
use tracing::info;
use wgpu::{
BufferAddress, BufferDescriptor, BufferUsages, Color, CommandEncoder, CommandEncoderDescriptor,
Device, Extent3d, ImageCopyBuffer, ImageCopyTexture, ImageDataLayout, LoadOp, Maintain,
@@ -30,19 +31,21 @@ pub fn render_preview_image(
model_pipeline: &mut ModelPipeline,
workspace: &WorkspaceRenderCallback,
) -> RgbaImage {
info!("Generating {}x{} preview image", size.0, size.1);
let (texture, depth_texture) = init_textures(device, size);
let texture_view = texture.create_view(&TextureViewDescriptor::default());
let depth_texture_view = depth_texture.create_view(&TextureViewDescriptor::default());
let (mut min, mut max) = (Vector3::repeat(f32::MAX), Vector3::repeat(f32::MIN));
for model in workspace.models.read().unwrap().iter() {
for model in workspace.models.read().iter() {
let (model_min, model_max) = model.mesh.minmax_point();
min = min.zip_map(&model_min, f32::min);
max = max.zip_map(&model_max, f32::max);
}
let target = (min + max) / 2.0;
let distance = (min - target).magnitude().max((max - target).magnitude());
let distance = (min - max).magnitude() / 2.0;
let mut old_workspace = workspace.clone();
old_workspace.camera = Camera {

View File

@@ -1,11 +1,14 @@
use std::sync::{Arc, Mutex, RwLock};
use std::sync::Arc;
use egui::PaintCallbackInfo;
use egui_wgpu::{CallbackResources, CallbackTrait, ScreenDescriptor};
use image::RgbaImage;
use nalgebra::{Matrix4, Vector3};
use parking_lot::{Mutex, RwLock};
use wgpu::{CommandBuffer, CommandEncoder, Device, Queue, RenderPass};
use crate::app::SliceOperation;
use super::{
camera::Camera,
pipelines::{
@@ -36,7 +39,7 @@ pub struct WorkspaceRenderCallback {
pub render_style: RenderStyle,
pub is_moving: bool,
pub render_preview: Option<Arc<Mutex<Option<RgbaImage>>>>,
pub slice_operation: Option<SliceOperation>,
}
impl CallbackTrait for WorkspaceRenderCallback {
@@ -50,18 +53,20 @@ impl CallbackTrait for WorkspaceRenderCallback {
) -> Vec<CommandBuffer> {
let resources = resources.get_mut::<WorkspaceRenderResources>().unwrap();
if let Some(preview) = &self.render_preview {
let image = render_preview_image(
device,
queue,
screen_descriptor,
encoder,
(512, 512),
&mut resources.model_pipeline,
self,
);
*preview.lock().unwrap() = Some(image);
match &self.slice_operation {
Some(slice_operation) if slice_operation.needs_preview_image() => {
let image = render_preview_image(
device,
queue,
screen_descriptor,
encoder,
(512, 512),
&mut resources.model_pipeline,
self,
);
slice_operation.add_preview_image(image);
}
_ => {}
}
resources

View File

@@ -17,7 +17,7 @@ pub fn ui(app: &mut App, ctx: &Context, _frame: &mut Frame) {
Window::new("Models")
.open(&mut app.windows.show_models)
.show(ctx, |ui| {
let mut meshes = app.meshes.write().unwrap();
let mut meshes = app.meshes.write();
let mut action = Action::None;
if meshes.is_empty() {

View File

@@ -9,7 +9,12 @@ use crate::{
};
pub fn ui(app: &mut App, ctx: &Context, _frame: &mut Frame) {
if let Some(result) = app.slice_result.lock().unwrap().as_mut() {
if let Some(slice_operation) = &app.slice_operation {
let mut result = slice_operation.result();
let Some(result) = result.as_mut() else {
return;
};
Window::new("Slice Preview").show(ctx, move |ui| {
ui.horizontal(|ui| {
ui.spacing_mut().slider_width = ui.available_size().x

View File

@@ -11,7 +11,9 @@ pub fn ui(app: &mut App, ctx: &Context, _frame: &mut Frame) {
let mut window_open = true;
let mut save_complete = false;
if let Some(progress) = app.slice_progress.as_ref() {
if let Some(slice_operation) = &app.slice_operation {
let progress = &slice_operation.progress;
let (current, total) = (progress.completed(), progress.total());
let mut window = Window::new("Slice Progress");
@@ -32,7 +34,9 @@ pub fn ui(app: &mut App, ctx: &Context, _frame: &mut Frame) {
} else {
ui.label("Slicing complete!");
if ui.button("Save").clicked() {
let result = app.slice_result.lock().unwrap().take().unwrap();
let result = app.slice_operation.as_ref().unwrap().result();
let result = result.as_ref().unwrap();
if let Some(path) = FileDialog::new().save_file() {
let mut file = File::create(path).unwrap();
let mut serializer = DynamicSerializer::new();
@@ -46,6 +50,6 @@ pub fn ui(app: &mut App, ctx: &Context, _frame: &mut Frame) {
}
if !window_open || save_complete {
app.slice_progress = None;
app.slice_operation = None;
}
}

View File

@@ -31,7 +31,6 @@ pub fn ui(app: &mut App, ctx: &Context, _frame: &mut Frame) {
app.meshes
.write()
.unwrap()
.push(RenderedMesh::from_mesh(model).with_name(name));
}
}