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, } 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::Rgba8Unorm, 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"); }