diff options
Diffstat (limited to 'src/main.rs')
| -rw-r--r-- | src/main.rs | 500 |
1 files changed, 148 insertions, 352 deletions
diff --git a/src/main.rs b/src/main.rs index a024ad3..3ce4ffc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,362 +1,117 @@ -use std::sync::Arc; -use winit::{ - application::ApplicationHandler, - event::WindowEvent, - event_loop::EventLoop, - keyboard::{KeyCode, PhysicalKey}, - window::Window, -}; - mod osc; +mod renderer; mod uniform; +mod window; -const VERTEX_SHADER: &str = "\ -@group(0) @binding(0) var<uniform> _wgsl_resolution: vec2<f32>; +use std::sync::Arc; -struct VertexOutput { - @builtin(position) clip_position: vec4<f32>, - @location(0) uv: vec2<f32>, - @location(1) resolution: vec2<f32>, -}; +use osc::{OscCommand, OscServer}; +use renderer::Renderer; +use window::{PreviewWindow, ScaleMode}; -@vertex -fn vs_main(@builtin(vertex_index) vi: u32) -> VertexOutput { - var uv = vec2<f32>(f32(vi % 2u), f32(vi / 2u)); - var out: VertexOutput; - out.clip_position = vec4<f32>(2.0 * uv - 1.0, 0.0, 1.0); - out.uv = vec2<f32>(uv.x, 1.0 - uv.y); - out.resolution = _wgsl_resolution; - return out; -} -"; +use winit::application::ApplicationHandler; +use winit::event::{ElementState, KeyEvent, WindowEvent}; +use winit::event_loop::EventLoop; +use winit::keyboard::{Key, NamedKey}; -const DEFAULT_FRAGMENT: &str = "\ -@fragment -fn fs_main(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> { - let check = floor(uv * 10.0); - return vec4<f32>(vec3<f32>(fract((check.x + check.y) / 2.0)), 1.0); -} -"; - -struct GpuState { - window: Arc<Window>, - surface: wgpu::Surface<'static>, +struct AppState { device: wgpu::Device, queue: wgpu::Queue, - config: wgpu::SurfaceConfiguration, - surface_format: wgpu::TextureFormat, - vertex_module: wgpu::ShaderModule, - vertex_bind_group_layout: wgpu::BindGroupLayout, - vertex_bind_group: wgpu::BindGroup, - resolution_buffer: wgpu::Buffer, - render_pipeline: wgpu::RenderPipeline, - uniform_cache: uniform::UniformCache, + renderer: Renderer, + preview: PreviewWindow, } struct App { - gpu: Option<GpuState>, - osc_server: osc::OscServer, -} - -fn load_shader( - device: &wgpu::Device, - surface_format: wgpu::TextureFormat, - vertex_module: &wgpu::ShaderModule, - vertex_bgl: &wgpu::BindGroupLayout, - fragment_source: &str, - cache: &mut uniform::UniformCache, -) -> Result<wgpu::RenderPipeline, String> { - let module = naga::front::wgsl::parse_str(fragment_source) - .map_err(|e| format!("WGSL parse error: {}", e))?; - - naga::valid::Validator::new( - naga::valid::ValidationFlags::all(), - naga::valid::Capabilities::all(), - ) - .validate(&module) - .map_err(|e| format!("WGSL validation error: {}", e))?; - - cache.refresh(module, device); - - let fragment_module = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: Some("fragment_shader"), - source: wgpu::ShaderSource::Wgsl(fragment_source.into()), - }); - - let mut bind_group_layouts: Vec<Option<&wgpu::BindGroupLayout>> = vec![Some(vertex_bgl)]; - if let Some(ref bgl) = cache.bind_group_layout { - bind_group_layouts.push(Some(bgl)); - } - - let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("pipeline_layout"), - bind_group_layouts: &bind_group_layouts, - immediate_size: 0, - }); - - let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("render_pipeline"), - layout: Some(&pipeline_layout), - vertex: wgpu::VertexState { - module: vertex_module, - entry_point: Some("vs_main"), - buffers: &[], - compilation_options: wgpu::PipelineCompilationOptions::default(), - }, - fragment: Some(wgpu::FragmentState { - module: &fragment_module, - entry_point: Some("fs_main"), - targets: &[Some(wgpu::ColorTargetState { - format: surface_format, - blend: Some(wgpu::BlendState::REPLACE), - write_mask: wgpu::ColorWrites::ALL, - })], - compilation_options: wgpu::PipelineCompilationOptions::default(), - }), - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleStrip, - strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, - cull_mode: None, - polygon_mode: wgpu::PolygonMode::Fill, - unclipped_depth: false, - conservative: false, - }, - depth_stencil: None, - multisample: wgpu::MultisampleState { - count: 1, - mask: !0, - alpha_to_coverage_enabled: false, - }, - multiview_mask: None, - cache: None, - }); - - Ok(pipeline) + width: u32, + height: u32, + scale_mode: ScaleMode, + osc: OscServer, + state: Option<AppState>, } impl App { - fn new() -> Self { - let osc_server = osc::OscServer::new("0.0.0.0:9000").expect("bind OSC socket"); - Self { - gpu: None, - osc_server, - } - } - - fn init_gpu(&mut self, window: Arc<Window>) { - let gpu = pollster::block_on(async { - let size = window.inner_size(); - let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { - backends: wgpu::Backends::VULKAN, - flags: wgpu::InstanceFlags::default(), - memory_budget_thresholds: wgpu::MemoryBudgetThresholds::default(), - backend_options: wgpu::BackendOptions::default(), - display: None, - }); - - let surface = instance - .create_surface(window.clone()) - .expect("create surface"); - - let adapter = instance - .request_adapter(&wgpu::RequestAdapterOptions { - power_preference: wgpu::PowerPreference::default(), - compatible_surface: Some(&surface), - force_fallback_adapter: false, - }) - .await - .expect("find adapter"); - - let (device, queue) = adapter - .request_device(&wgpu::DeviceDescriptor::default()) - .await - .expect("create device"); - - 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 config = wgpu::SurfaceConfiguration { - usage: wgpu::TextureUsages::RENDER_ATTACHMENT, - format: surface_format, - width: size.width.max(1), - height: size.height.max(1), - present_mode: wgpu::PresentMode::Fifo, - alpha_mode: surface_caps.alpha_modes[0], - view_formats: vec![], - desired_maximum_frame_latency: 2, - }; - surface.configure(&device, &config); - - let mut cache = uniform::UniformCache::new(); - let vertex_module = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: Some("vertex_shader"), - source: wgpu::ShaderSource::Wgsl(VERTEX_SHADER.into()), - }); - - let resolution_buffer = device.create_buffer(&wgpu::BufferDescriptor { - label: Some("resolution_buffer"), - size: 8, - usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, - mapped_at_creation: false, - }); - - let vertex_bind_group_layout = - device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: Some("vertex_bind_group_layout"), - entries: &[wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::VERTEX, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }], - }); - - let vertex_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - label: Some("vertex_bind_group"), - layout: &vertex_bind_group_layout, - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: resolution_buffer.as_entire_binding(), - }], - }); - - let pipeline = load_shader( - &device, - surface_format, - &vertex_module, - &vertex_bind_group_layout, - DEFAULT_FRAGMENT, - &mut cache, - ) - .expect("default shader should compile"); - - GpuState { - window, - surface, - device, - queue, - config, - surface_format, - vertex_module, - vertex_bind_group_layout, - vertex_bind_group, - resolution_buffer, - render_pipeline: pipeline, - uniform_cache: cache, - } - }); - - self.gpu = Some(gpu); - } - fn handle_osc(&mut self) { - let gpu = self.gpu.as_mut().expect("gpu initialized"); - - let commands = self.osc_server.poll(&mut gpu.uniform_cache); - for cmd in commands { - match cmd { - osc::OscCommand::Shader(code) => { - match load_shader( - &gpu.device, - gpu.surface_format, - &gpu.vertex_module, - &gpu.vertex_bind_group_layout, - &code, - &mut gpu.uniform_cache, - ) { - Ok(pipeline) => { - gpu.render_pipeline = pipeline; - log::info!("shader loaded successfully"); - } - Err(e) => log::error!("{}", e), - } + let state = self.state.as_mut().expect("gpu not initialized"); + self.osc.poll(|cmd| match cmd { + OscCommand::Shader(source) => { + if let Err(e) = state.renderer.load_shader(&state.device, &source) { + log::error!("shader error: {}", e); } } - } - } - - fn render(&mut self) { - let gpu = self.gpu.as_mut().expect("gpu initialized"); - - gpu.uniform_cache.flush(&gpu.queue); - - let output = match gpu.surface.get_current_texture() { - wgpu::CurrentSurfaceTexture::Success(t) - | wgpu::CurrentSurfaceTexture::Suboptimal(t) => t, - wgpu::CurrentSurfaceTexture::Outdated | wgpu::CurrentSurfaceTexture::Lost => { - gpu.surface.configure(&gpu.device, &gpu.config); - return; - } - other => { - log::error!("surface error: {:?}", other); - return; - } - }; - - let view = output - .texture - .create_view(&wgpu::TextureViewDescriptor::default()); - - let mut encoder = gpu - .device - .create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some("render_encoder"), - }); - - { - let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("render_pass"), - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: &view, - depth_slice: None, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color::BLACK), - store: wgpu::StoreOp::Store, - }, - })], - depth_stencil_attachment: None, - occlusion_query_set: None, - timestamp_writes: None, - multiview_mask: None, - }); - - pass.set_pipeline(&gpu.render_pipeline); - let resolution = [gpu.config.width as f32, gpu.config.height as f32]; - gpu.queue - .write_buffer(&gpu.resolution_buffer, 0, bytemuck::cast_slice(&resolution)); - pass.set_bind_group(0, &gpu.vertex_bind_group, &[]); - if let Some(ref bg) = gpu.uniform_cache.bind_group { - pass.set_bind_group(1, bg, &[]); + OscCommand::Uniform { path, args } => { + if let Err(e) = state.renderer.set_uniform(&path, &args) { + log::warn!("uniform error: {}", e); + } } - pass.draw(0..4, 0..1); - } - - gpu.queue.submit(std::iter::once(encoder.finish())); - output.present(); + }); } } impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) { - if self.gpu.is_none() { - let window = Arc::new( - event_loop - .create_window(Window::default_attributes().with_title("wgsl-view")) - .expect("create window"), - ); - self.init_gpu(window); - } + let window = Arc::new( + event_loop + .create_window( + winit::window::Window::default_attributes() + .with_title("wgsl-view") + .with_inner_size(winit::dpi::LogicalSize::new(self.width, self.height)), + ) + .expect("create window"), + ); + + let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { + backends: wgpu::Backends::VULKAN, + flags: wgpu::InstanceFlags::default(), + backend_options: wgpu::BackendOptions::default(), + memory_budget_thresholds: wgpu::MemoryBudgetThresholds::default(), + display: None, + }); + + let surface = instance + .create_surface(window.clone()) + .expect("create surface"); + + let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions { + power_preference: wgpu::PowerPreference::HighPerformance, + compatible_surface: Some(&surface), + force_fallback_adapter: false, + })) + .expect("find adapter"); + + let (device, queue) = pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor { + label: Some("device"), + required_features: wgpu::Features::empty(), + required_limits: wgpu::Limits::default(), + ..Default::default() + })) + .expect("create device"); + + 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 renderer = Renderer::new(&device, &queue, self.width, self.height); + let preview = PreviewWindow::new( + &device, + window, + surface, + surface_format, + renderer.canvas_view(), + self.width, + self.height, + self.scale_mode, + ); + + self.state = Some(AppState { + device, + queue, + renderer, + preview, + }); } fn window_event( @@ -365,44 +120,85 @@ impl ApplicationHandler for App { _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: - winit::event::KeyEvent { - physical_key: PhysicalKey::Code(KeyCode::Escape), - state: winit::event::ElementState::Pressed, + KeyEvent { + logical_key, + state: ElementState::Pressed, .. }, .. - } => event_loop.exit(), + } => 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) => { - let gpu = self.gpu.as_mut().expect("gpu initialized"); - gpu.config.width = size.width.max(1); - gpu.config.height = size.height.max(1); - gpu.surface.configure(&gpu.device, &gpu.config); + state.preview.resize(&state.device, size.width, size.height); } WindowEvent::RedrawRequested => { self.handle_osc(); - self.render(); + let state = self.state.as_mut().expect("gpu not initialized"); + state.renderer.render(&state.device, &state.queue); + state.preview.draw(&state.device, &state.queue); } _ => {} } } fn about_to_wait(&mut self, _event_loop: &winit::event_loop::ActiveEventLoop) { - self.gpu - .as_ref() - .expect("gpu initialized") - .window - .request_redraw(); + if let Some(ref state) = self.state { + state.preview.window().request_redraw(); + } } } fn main() { env_logger::init(); + + let mut args = std::env::args().skip(1); + let mut width = 1920u32; + let mut height = 1080u32; + let mut scale_mode = ScaleMode::Contain; + let mut port = 9000u16; + + while let Some(arg) = args.next() { + match arg.as_str() { + "--width" => width = args.next().expect("--width VALUE").parse().expect("u32"), + "--height" => height = args.next().expect("--height VALUE").parse().expect("u32"), + "--port" => port = args.next().expect("--port VALUE").parse().expect("u16"), + "--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 osc = OscServer::new(&format!("0.0.0.0:{}", port)).expect("bind OSC socket"); + let event_loop = EventLoop::new().expect("create event loop"); event_loop.set_control_flow(winit::event_loop::ControlFlow::Poll); - let mut app = App::new(); - event_loop.run_app(&mut app).expect("run event loop"); + + let mut app = App { + width, + height, + scale_mode, + osc, + state: None, + }; + + event_loop.run_app(&mut app).expect("run"); } |
