summaryrefslogtreecommitdiffstats
path: root/src/bin/tsv_view.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/bin/tsv_view.rs')
-rw-r--r--src/bin/tsv_view.rs220
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");
+}