use std::rc::Rc; use crate::mvu::{Event, File, MvuApp, Size2i, FrameTimer}; use { log::info, std::borrow::Cow, wgpu::{Device, Instance, Queue, RenderPipeline, Surface, SurfaceConfiguration, Trace}, }; mod dem_renderer; mod raster; use dem_renderer::DemRenderer; #[derive(Clone)] pub struct Model { dem: Option>, } impl Model { pub fn new() -> Model { Model { dem: None } } } struct Context { config: SurfaceConfiguration, surface: Surface<'static>, device: Device, render_pipeline: RenderPipeline, queue: Queue, scene_data: Option, frame_timer: Box, } #[derive(Default)] pub struct App { context: Option, } impl App { pub fn new() -> Self { Self::default() } } impl MvuApp for App { async fn init( &mut self, instance: &Instance, surface: Surface<'static>, size: Size2i, frame_timer: Box, ) { let adapter = instance .request_adapter(&wgpu::RequestAdapterOptions { power_preference: wgpu::PowerPreference::HighPerformance, force_fallback_adapter: false, compatible_surface: Some(&surface), }) .await .expect("Failed to find an appropriate adapter"); 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()), memory_hints: wgpu::MemoryHints::MemoryUsage, trace: Trace::Off, }) .await .expect("Failed to create device"); let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { label: None, source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))), }); let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: None, bind_group_layouts: &[], push_constant_ranges: &[], }); let swapchain_capabilities = surface.get_capabilities(&adapter); let swapchain_format = swapchain_capabilities.formats[0]; let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { label: None, layout: Some(&pipeline_layout), vertex: wgpu::VertexState { module: &shader, entry_point: Some("vs_main"), buffers: &[], compilation_options: Default::default(), }, fragment: Some(wgpu::FragmentState { module: &shader, entry_point: Some("fs_main"), compilation_options: Default::default(), targets: &[Some(swapchain_format.into())], }), primitive: wgpu::PrimitiveState::default(), depth_stencil: None, multisample: wgpu::MultisampleState::default(), multiview: None, cache: None, }); let mut config = surface .get_default_config(&adapter, size.width, size.height) .expect("get default graphics surface"); config.present_mode = wgpu::PresentMode::AutoVsync; config.view_formats.push(config.format); surface.configure(&device, &config); self.context = Some(Context { config, surface, device, render_pipeline, queue, scene_data: None, frame_timer: frame_timer, }); info!("Initialized {}x{}.", size.width, size.height); } async fn resize(&mut self, new_size: Size2i) { if let Some(Context { config, surface, device, .. }) = &mut self.context { config.width = new_size.width.max(1); config.height = new_size.height.max(1); surface.configure(device, config); info!("Resized_to {}x{}.", new_size.width, new_size.height); } } async fn update(&self, model: Rc, event: Event) -> Rc { match event { Event::MouseButtonPressed => model, Event::OpenTestFile(file_path) => open_test_file(file_path, model.clone()), } } async fn view(&mut self, model: Rc) -> Result<(), Box> { if let Some(context) = &mut self.context { context.frame_timer.mark_frame_start(); if context.scene_data.is_none() { if let Some(dem) = &model.dem { context.scene_data = Some(DemRenderer::new( dem.clone(), &context.device, &context.config, &context.queue, )) } } let frame = context .surface .get_current_texture() .expect("Failed to acquire next swap chain texture"); let view = frame .texture .create_view(&wgpu::TextureViewDescriptor::default()); if let Some(scene_data) = &mut context.scene_data { scene_data.render( context.frame_timer.as_ref(), &view, &context.device, &context.queue, ); } else { let mut encoder = context .device .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); { let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: None, color_attachments: &[Some(wgpu::RenderPassColorAttachment { view: &view, resolve_target: None, ops: wgpu::Operations { load: wgpu::LoadOp::Clear(wgpu::Color::GREEN), store: wgpu::StoreOp::Store, }, })], depth_stencil_attachment: None, timestamp_writes: None, occlusion_query_set: None, }); rpass.set_pipeline(&context.render_pipeline); rpass.draw(0..3, 0..1); } context.queue.submit(Some(encoder.finish())); } frame.present(); } Ok(()) } } fn open_test_file(file: Box, model: Rc) -> Rc { let dem = Some(Rc::new(raster::Dem::load_from_image(file))); Rc::new(Model { dem, ..*model }) }