diff options
Diffstat (limited to 'src/bin/tsv_view.rs')
| -rw-r--r-- | src/bin/tsv_view.rs | 220 |
1 files changed, 220 insertions, 0 deletions
diff --git a/src/bin/tsv_view.rs b/src/bin/tsv_view.rs new file mode 100644 index 0000000..885a8c4 --- /dev/null +++ b/src/bin/tsv_view.rs @@ -0,0 +1,220 @@ +use std::sync::Arc; + +use ash::vk; +use wgsl_view::gpu; +use wgsl_view::window::{PreviewWindow, ScaleMode}; + +use winit::application::ApplicationHandler; +use winit::event::{ElementState, KeyEvent, WindowEvent}; +use winit::event_loop::EventLoop; +use winit::keyboard::{Key, NamedKey}; + +struct AppState { + device: wgpu::Device, + queue: wgpu::Queue, + preview: PreviewWindow, + client: texture_share_vk_client::VkClient, + fence: vk::Fence, + canvas: wgpu::Texture, + name: String, +} + +struct App { + scale_mode: ScaleMode, + name: String, + state: Option<AppState>, +} + +impl ApplicationHandler for App { + fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) { + let instance = gpu::create_instance(); + + // Create a temporary headless device to connect to TSV and read image dimensions + let temp_adapter = gpu::create_adapter(&instance, None); + let (temp_device, _temp_queue) = gpu::create_device(&temp_adapter); + let mut client = gpu::create_tsv_client(&temp_device); + + let (width, height) = match client.find_image_data(&self.name, true) { + Ok(Some((_lock, data))) => (data.width, data.height), + Ok(None) => { + log::error!("tsv image '{}' not found", self.name); + event_loop.exit(); + return; + } + Err(e) => { + log::error!("failed to find tsv image '{}': {e}", self.name); + event_loop.exit(); + return; + } + }; + log::info!("found tsv image '{}': {width}x{height}", self.name); + + drop(client); + drop(temp_device); + + let window = Arc::new( + event_loop + .create_window( + winit::window::Window::default_attributes() + .with_title(format!("tsv-view: {}", self.name)) + .with_inner_size(winit::dpi::LogicalSize::new(width, height)), + ) + .expect("create window"), + ); + + let surface = instance + .create_surface(window.clone()) + .expect("create surface"); + let adapter = gpu::create_adapter(&instance, Some(&surface)); + let (device, queue) = gpu::create_device(&adapter); + + let surface_caps = surface.get_capabilities(&adapter); + let surface_format = surface_caps + .formats + .iter() + .find(|f| f.is_srgb()) + .copied() + .unwrap_or(surface_caps.formats[0]); + + let mut client = gpu::create_tsv_client(&device); + client.find_image(&self.name, true).expect("find tsv image"); + + let fence = gpu::create_fence(&client); + + let canvas = device.create_texture(&wgpu::TextureDescriptor { + label: Some("tsv_canvas"), + size: wgpu::Extent3d { + width, + height, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Rgba8UnormSrgb, + usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, + view_formats: &[], + }); + + let canvas_view = canvas.create_view(&Default::default()); + let preview = PreviewWindow::new( + &device, + window, + surface, + surface_format, + &canvas_view, + width, + height, + self.scale_mode, + ); + + self.state = Some(AppState { + device, + queue, + preview, + client, + fence, + canvas, + name: self.name.clone(), + }); + } + + fn window_event( + &mut self, + event_loop: &winit::event_loop::ActiveEventLoop, + _window_id: winit::window::WindowId, + event: WindowEvent, + ) { + let state = self.state.as_mut().expect("gpu not initialized"); + + match event { + WindowEvent::CloseRequested => event_loop.exit(), + WindowEvent::KeyboardInput { + event: + KeyEvent { + logical_key, + state: ElementState::Pressed, + .. + }, + .. + } => match logical_key { + Key::Named(NamedKey::Escape) => event_loop.exit(), + Key::Character(ref c) if c.as_str() == "c" => { + state.preview.scale_mode = state.preview.scale_mode.cycle(); + log::info!("scale mode: {:?}", state.preview.scale_mode); + } + _ => {} + }, + WindowEvent::Resized(size) => { + state.preview.resize(&state.device, size.width, size.height); + } + WindowEvent::RedrawRequested => { + // Receive the latest frame from texture-share-vk. + // Use UNDEFINED as orig_layout: the image content is fully overwritten by recv, + // and wgpu may not have transitioned it yet. + let canvas_image = unsafe { gpu::raw_image(&state.canvas) }; + match state.client.recv_image( + &state.name, + canvas_image, + vk::ImageLayout::UNDEFINED, + vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL, + state.fence, + ) { + Err(e) => log::warn!("recv_image error: {e}"), + Ok(Some(())) => log::debug!("got frame"), + Ok(None) => {} + } + + state.preview.draw(&state.device, &state.queue); + } + _ => {} + } + } + + fn about_to_wait(&mut self, _event_loop: &winit::event_loop::ActiveEventLoop) { + if let Some(ref state) = self.state { + state.preview.window().request_redraw(); + } + } +} + +impl Drop for AppState { + fn drop(&mut self) { + gpu::destroy_fence(&self.client, self.fence); + } +} + +fn main() { + env_logger::init(); + + let mut args = std::env::args().skip(1); + let mut scale_mode = ScaleMode::Contain; + let mut name = "wgsl-view".to_string(); + + while let Some(arg) = args.next() { + match arg.as_str() { + "--name" => name = args.next().expect("--name VALUE"), + "--scale" => { + scale_mode = match args.next().expect("--scale VALUE").as_str() { + "contain" => ScaleMode::Contain, + "cover" => ScaleMode::Cover, + "center" => ScaleMode::Center, + "natural" => ScaleMode::Natural, + s => panic!("unknown scale mode: {s}"), + } + } + other => panic!("unknown argument: {other}"), + } + } + + let event_loop = EventLoop::new().expect("create event loop"); + event_loop.set_control_flow(winit::event_loop::ControlFlow::Poll); + + let mut app = App { + scale_mode, + name, + state: None, + }; + + event_loop.run_app(&mut app).expect("run"); +} |
