Compare commits

...

9 Commits

13 changed files with 203 additions and 69 deletions

1
.gitattributes vendored
View File

@ -1,2 +1,3 @@
*.jpg filter=lfs diff=lfs merge=lfs -text
*.tiff filter=lfs diff=lfs merge=lfs -text
*.data filter=lfs diff=lfs merge=lfs -text

View File

@ -11,7 +11,7 @@ use {
};
pub struct DemRenderer {
source: Rc<Dem>,
pub source: Rc<Dem>,
pipeline: wgpu::RenderPipeline,
bind_group: wgpu::BindGroup,
vertex_buffer: wgpu::Buffer,
@ -577,5 +577,5 @@ fn get_animated_camera_position(animation_phase: f32, dem_size: f32) -> glam::Ve
)
}
#[cfg(test)]
mod tests;
//#[cfg(test)]
//mod tests;

View File

@ -23,8 +23,8 @@ async fn run_compute_shader_test(
required_features: wgpu::Features::empty(),
required_limits: wgpu::Limits::downlevel_defaults(),
memory_hints: wgpu::MemoryHints::MemoryUsage,
trace: wgpu::Trace::Off,
},
None,
)
.await
.unwrap();
@ -87,7 +87,7 @@ async fn run_compute_shader_test(
let (tx, rx) = channel();
let buffer_slice = staging_buffer.slice(..);
buffer_slice.map_async(wgpu::MapMode::Read, move |v| tx.send(v).unwrap());
device.poll(wgpu::Maintain::wait()).panic_on_timeout();
assert!(device.poll(wgpu::PollType::wait()).unwrap().wait_finished());
if let Ok(Ok(())) = rx.recv() {
buffer_slice.get_mapped_range().to_vec()
} else {
@ -99,7 +99,7 @@ async fn run_compute_shader_test(
#[derive(Clone, Copy, Pod, Zeroable)]
struct Vec3 {
elements: [f32; 3],
_padding: f32
_padding: f32,
}
#[repr(C)]
@ -114,7 +114,7 @@ struct TestInput {
fn vec3(x: f32, y: f32, z: f32) -> Vec3 {
Vec3 {
elements: [x, y, z],
_padding: 0.0
_padding: 0.0,
}
}

View File

@ -1,6 +1,6 @@
use std::rc::Rc;
use crate::mvu::{Event, File, MvuApp, Size2i, FrameTimer};
use crate::mvu::{Event, File, FrameTimer, MvuApp, Size2i};
use {
log::info,
std::borrow::Cow,
@ -11,6 +11,9 @@ mod dem_renderer;
mod raster;
use dem_renderer::DemRenderer;
mod statistics_reporter;
use statistics_reporter::StatisticsReporter;
#[derive(Clone)]
pub struct Model {
dem: Option<Rc<raster::Dem>>,
@ -30,6 +33,7 @@ struct Context {
queue: Queue,
scene_data: Option<DemRenderer>,
frame_timer: Box<dyn FrameTimer>,
statistics_reporter: StatisticsReporter,
}
#[derive(Default)]
@ -59,14 +63,17 @@ impl MvuApp<Model> for App {
})
.await
.expect("Failed to find an appropriate adapter");
info!("Using {} backend with {}", adapter.get_info().backend, adapter.get_info().name);
info!(
"Using {} backend with {}",
adapter.get_info().backend,
adapter.get_info().name
);
let (device, queue) = adapter
.request_device(&wgpu::DeviceDescriptor {
label: None,
required_features: wgpu::Features::empty(),
required_limits: wgpu::Limits::default()
.using_resolution(adapter.limits()),
required_limits: wgpu::Limits::default().using_resolution(adapter.limits()),
memory_hints: wgpu::MemoryHints::MemoryUsage,
trace: Trace::Off,
})
@ -123,7 +130,8 @@ impl MvuApp<Model> for App {
render_pipeline,
queue,
scene_data: None,
frame_timer: frame_timer,
frame_timer,
statistics_reporter: StatisticsReporter::new(),
});
info!("Initialized {}x{}.", size.width, size.height);
@ -149,14 +157,22 @@ impl MvuApp<Model> for App {
match event {
Event::MouseButtonPressed => model,
Event::OpenTestFile(file_path) => open_test_file(file_path, model.clone()),
Event::ClearScene => clear_scene(model.clone()),
}
}
async fn view(&mut self, model: Rc<Model>) -> Result<(), Box<dyn std::error::Error>> {
if let Some(context) = &mut self.context {
context.frame_timer.mark_frame_start();
if context.scene_data.is_none() {
context
.statistics_reporter
.log_frame_time_seconds(context.frame_timer.get_frame_time_seconds());
if let Some(dem) = &model.dem {
if context
.scene_data
.as_ref()
.is_none_or(|scene_data| scene_data.source.id != dem.id)
{
context.scene_data = Some(DemRenderer::new(
dem.clone(),
&context.device,
@ -164,6 +180,8 @@ impl MvuApp<Model> for App {
&context.queue,
))
}
} else {
context.scene_data = None;
}
let frame = context
@ -215,3 +233,10 @@ fn open_test_file(file: Box<dyn File>, model: Rc<Model>) -> Rc<Model> {
let dem = Some(Rc::new(raster::Dem::load_from_image(file)));
Rc::new(Model { dem, ..*model })
}
fn clear_scene(model: Rc<Model>) -> Rc<Model> {
Rc::new(Model {
dem: None,
..*model
})
}

View File

@ -1,4 +1,5 @@
use crate::mvu::File;
use std::sync::atomic::{AtomicU32, Ordering};
#[derive(Clone)]
pub struct Dem {
@ -11,6 +12,7 @@ pub struct Dem {
pub z_min: f32,
pub z_max: f32,
pub grid: Vec<u16>,
pub id: Id,
}
impl std::fmt::Debug for Dem {
@ -46,6 +48,18 @@ pub struct DemBvhLayer {
pub data: Vec<u16>,
}
#[derive(Clone, Eq, PartialEq)]
pub struct Id(u32);
impl Id {
fn get_next() -> Self {
NEXT_ID.fetch_add(1, Ordering::SeqCst);
Self(NEXT_ID.load(Ordering::SeqCst))
}
}
static NEXT_ID: AtomicU32 = AtomicU32::new(0);
/// Find the smallest number that's larger then the input and that is also one
/// less than a power of two.
fn round_bound(v: u32) -> u32 {
@ -198,6 +212,7 @@ impl Dem {
z_min,
z_max,
grid,
id: Id::get_next(),
}
}
_ => {
@ -283,6 +298,7 @@ mod tests {
z_min: z1.min(z2),
z_max: z1.max(z2),
grid,
id: Id::get_next()
},
)
.boxed()

View File

@ -15,6 +15,7 @@ impl File for std::io::Cursor<Vec<u8>> {}
pub enum Event {
MouseButtonPressed,
OpenTestFile(Box<dyn File>),
ClearScene,
}
#[derive(Debug, Clone, Copy)]

View File

@ -112,6 +112,16 @@ impl PteropusCanvas {
.await;
}
#[wasm_bindgen]
pub async fn clear_scene(&mut self) {
self.model = self
.app
.lock()
.expect("get app mutex")
.update(Rc::clone(&self.model), Event::ClearScene)
.await;
}
pub fn get_canvas(&self) -> HtmlCanvasElement {
let window = web_sys::window().expect("get window");
let document = window.document().expect("get HTML document");

View File

@ -2,61 +2,20 @@
<html>
<head>
<title>Pteropus</title>
<style>
#pteropus-canvas {
height: 100%;
width: 100%;
display: block;
}
<link rel="stylesheet" href="pteropus-frame.css">
</style>
</head>
<body>
<div id="whole-window">
<script type="module" src="pteropus-frame.js"></script>
<canvas id="pteropus-canvas"></canvas>
<script type="module">
import init, {init_pteropus} from '../pkg/pteropus.js';
function fileDragOverHandler(event) {
event.preventDefault();
}
async function run() {
await init();
let pteropus = await init_pteropus();
let test_file_data = null;
let needs_resize = true;
let mainCanvas = document.getElementById("pteropus-canvas");
mainCanvas.addEventListener("drop", async (event) => {
event.preventDefault();
test_file_data = event.dataTransfer.files[0]
});
mainCanvas.addEventListener("dragover", fileDragOverHandler);
const resizeObserver = new ResizeObserver((entries) => {
needs_resize = true;
});
resizeObserver.observe(mainCanvas)
while(true)
{
await pteropus.render();
// TODO: I really want to do this asynchronously, with the drop
// event listener calling pteropus_load_file directly, but I need
// to figure out how to get that to work.
if(test_file_data) {
await pteropus.load_file(test_file_data);
test_file_data = null
}
if(needs_resize) {
await pteropus.on_resize();
needs_resize = false;
}
await new Promise(requestAnimationFrame);
}
}
run();
</script>
<div id="bottom-panel">
<input type="file" id="file-upload-file-input" accept="image/tiff, .tif, .tiff">
<button id="clear-scene-button">Clear Scene</button>
<button id="load-small-sample-button">Load Small Sample</button>
<button id="load-medium-sample-button">Load Medium Sample</button>
<button id="load-large-sample-button">Load Large Sample</button>
</div>
</div>
</body>
</html>

BIN
web/large_sample.tiff (Stored with Git LFS) Normal file

Binary file not shown.

BIN
web/medium_sample.tiff (Stored with Git LFS) Normal file

Binary file not shown.

27
web/pteropus-frame.css Normal file
View File

@ -0,0 +1,27 @@
body {
margin: 0;
}
#whole-window {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
}
#pteropus-canvas {
display: flex;
flex-grow: 1;
margin: 0.4rem;
}
#bottom-panel {
display: flex;
flex-grow: 0;
margin: 0.4rem;
}
button {
margin-left: 0.2rem;
margin-right: 0.2rem;
}

86
web/pteropus-frame.js Normal file
View File

@ -0,0 +1,86 @@
import init, {init_pteropus} from '../pkg/pteropus.js';
function fileDragOverHandler(event) {
event.preventDefault();
}
async function run() {
await init();
let pteropus = await init_pteropus();
let test_file_data = null;
let needs_resize = true;
let clear_scene = false;
let mainCanvas = document.getElementById("pteropus-canvas");
mainCanvas.addEventListener("drop", async (event) => {
event.preventDefault();
test_file_data = event.dataTransfer.files[0]
});
mainCanvas.addEventListener("dragover", fileDragOverHandler);
const resizeObserver = new ResizeObserver((entries) => {
needs_resize = true;
});
resizeObserver.observe(mainCanvas);
let fileUploadButton = document.getElementById("file-upload-file-input");
fileUploadButton.addEventListener("change", () => {
const files = fileUploadButton.files;
if(files.length > 0) {
test_file_data = files[0]
}
});
let clearSceneButton = document.getElementById("clear-scene-button");
clearSceneButton.addEventListener("click", () => {
clear_scene = true;
});
[
{
buttonId: "load-small-sample-button",
url: "small_sample.tiff"
},
{
buttonId: "load-medium-sample-button",
url: "medium_sample.tiff"
},
{
buttonId: "load-large-sample-button",
url: "large_sample.tiff"
}
].forEach( (sample) => {
let loadSampleButton = document.getElementById(sample.buttonId);
loadSampleButton.addEventListener("click", async () => {
const response = await fetch(sample.url);
if(!response.ok) {
console.error("Couild not load sample file.")
}
test_file_data = await response.blob()
});
});
while(true)
{
await pteropus.render();
if(needs_resize) {
await pteropus.on_resize();
needs_resize = false;
}
if(clear_scene) {
await pteropus.clear_scene();
clear_scene = false;
}
// TODO: I really want to do this asynchronously, with the drop
// event listener calling pteropus_load_file directly, but I need
// to figure out how to get that to work.
if(test_file_data) {
await pteropus.load_file(test_file_data);
test_file_data = null
}
await new Promise(requestAnimationFrame);
}
}
run();

BIN
web/small_sample.tiff (Stored with Git LFS) Normal file

Binary file not shown.