// use super::render_state::RenderState; // use super::program_state::ProgramState; use super::color_palette::ColorPalette; use super::gui_rect::GUIRect; use super::pipeline::Pipeline; use super::pipeline::PipelineDetails; use super::shader_cache::ShaderCache; use super::texture::Texture; use std::collections::VecDeque; use winit::event::*; use winit::event_loop::ControlFlow; use winit::event_loop::EventLoop; use winit::window::Window; pub struct Application { pub surface: wgpu::Surface, pub adapter: wgpu::Adapter, pub device: wgpu::Device, pub queue: wgpu::Queue, pub swap_chain_descriptor: wgpu::SwapChainDescriptor, pub swap_chain: wgpu::SwapChain, pub shader_cache: ShaderCache, // pub texture_cache: TextureCache, pub gui_rect_queue: VecDeque, pub pipeline_queue: VecDeque, pub temp_color_toggle: bool, } impl Application { pub fn new(window: &Window) -> Self { // Window as understood by WGPU for rendering onto let surface = wgpu::Surface::create(window); // Represents a GPU, exposes the real GPU device and queue let adapter = wgpu::Adapter::request(&wgpu::RequestAdapterOptions { ..Default::default() }).unwrap(); // Requests the device and queue from the adapter let requested_device = adapter.request_device(&wgpu::DeviceDescriptor { extensions: wgpu::Extensions { anisotropic_filtering: false }, limits: Default::default(), }); // Connection to the physical GPU let device = requested_device.0; // Represents the GPU command queue, to submit CommandBuffers let queue = requested_device.1; // Properties for the swap chain frame buffers let swap_chain_descriptor = wgpu::SwapChainDescriptor { usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, format: wgpu::TextureFormat::Bgra8UnormSrgb, width: window.inner_size().width, height: window.inner_size().height, present_mode: wgpu::PresentMode::Vsync, }; // Series of frame buffers with images presented to the surface let swap_chain = device.create_swap_chain(&surface, &swap_chain_descriptor); // Cache of all loaded shaders let shader_cache = ShaderCache::new(); let gui_rect_queue = VecDeque::new(); let pipeline_queue = VecDeque::new(); Self { surface, adapter, device, queue, swap_chain_descriptor, swap_chain, shader_cache, gui_rect_queue, pipeline_queue, temp_color_toggle: true, } } pub fn example(&mut self) { self.shader_cache.load(&self.device, "shaders/shader.vert", glsl_to_spirv::ShaderType::Vertex).unwrap(); self.shader_cache.load(&self.device, "shaders/shader.frag", glsl_to_spirv::ShaderType::Fragment).unwrap(); let vertex_shader = self.shader_cache.get_by_path("shaders/shader.vert").unwrap(); let fragment_shader = self.shader_cache.get_by_path("shaders/shader.frag").unwrap(); let texture_view = Texture::from_filepath(&self.device, &mut self.queue, "textures/grid.png").unwrap().view; let example_pipeline = Pipeline::new(&self.device, PipelineDetails { vertex_shader, fragment_shader, texture_view: Some(&texture_view), }); self.pipeline_queue.push_back(example_pipeline); } pub fn begin_lifecycle(mut self, event_loop: EventLoop<()>, window: Window) { event_loop.run(move |event, _, control_flow| self.main_event_loop(event, control_flow, &window)); } pub fn main_event_loop(&mut self, event: Event<'_, T>, control_flow: &mut ControlFlow, window: &Window) { match event { // Handle all window events in sequence Event::WindowEvent { ref event, window_id } if window_id == window.id() => { self.window_event(event, control_flow); }, // After handling every event and updating the GUI, request a new sequence of draw commands Event::MainEventsCleared => { // Turn the GUI changes into draw commands added to the render pipeline queue self.redraw(); // If any draw commands were actually added, ask the window to issue a redraw event if !self.pipeline_queue.is_empty() { window.request_redraw(); } *control_flow = ControlFlow::Wait; }, // Resizing or calling `window.request_redraw()` now redraws the GUI with the pipeline queue Event::RedrawRequested(_) => { self.render(); *control_flow = ControlFlow::Wait; }, // Catch extraneous events _ => { *control_flow = ControlFlow::Wait; }, } } pub fn window_event(&mut self, event: &WindowEvent, control_flow: &mut ControlFlow) { match event { WindowEvent::CloseRequested => { self.quit(control_flow); }, WindowEvent::KeyboardInput { input, .. } => { self.keyboard_event(input, control_flow); }, WindowEvent::Resized(physical_size) => { self.resize(*physical_size); *control_flow = ControlFlow::Wait; }, WindowEvent::ScaleFactorChanged { new_inner_size, .. } => { self.resize(**new_inner_size); *control_flow = ControlFlow::Wait; }, _ => { *control_flow = ControlFlow::Wait; }, } } pub fn keyboard_event(&mut self, input: &KeyboardInput, control_flow: &mut ControlFlow) { match input { KeyboardInput { state: ElementState::Pressed, virtual_keycode: Some(VirtualKeyCode::Escape), .. } => { self.quit(control_flow); }, KeyboardInput { state: ElementState::Pressed, virtual_keycode: Some(VirtualKeyCode::Space), .. } => { self.example(); }, _ => { *control_flow = ControlFlow::Wait; }, } } pub fn quit(&self, control_flow: &mut ControlFlow) { *control_flow = ControlFlow::Exit; } pub fn resize(&mut self, new_size: winit::dpi::PhysicalSize) { self.swap_chain_descriptor.width = new_size.width; self.swap_chain_descriptor.height = new_size.height; self.swap_chain = self.device.create_swap_chain(&self.surface, &self.swap_chain_descriptor); // TODO: Mark root of GUI as dirty to force redraw of everything } // Traverse the dirty GUI elements and queue up pipelines to render each GUI rectangle (box/sprite) pub fn redraw(&mut self) { } // Render the queue of pipeline draw commands over the current window pub fn render(&mut self) { // Turn the queue of pipelines each into a command buffer and submit it to the render queue while !self.pipeline_queue.is_empty() { // Get a frame buffer to render on let frame = self.swap_chain.get_next_texture(); // Get the pipeline to render in this iteration let pipeline_struct = self.pipeline_queue.pop_back().unwrap(); // Generates a render pass that commands are applied to, then generates a command buffer when finished let mut command_encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { todo: 0 }); // Temporary way to swap clear color every render let color = match self.temp_color_toggle { true => ColorPalette::get_color_linear(ColorPalette::MildBlack), false => ColorPalette::get_color_linear(ColorPalette::NearBlack), }; self.temp_color_toggle = !self.temp_color_toggle; // Recording of commands while in "rendering mode" that go into a command buffer let mut render_pass = command_encoder.begin_render_pass(&wgpu::RenderPassDescriptor { color_attachments: &[ wgpu::RenderPassColorAttachmentDescriptor { attachment: &frame.view, resolve_target: None, load_op: wgpu::LoadOp::Clear, store_op: wgpu::StoreOp::Store, clear_color: color, } ], depth_stencil_attachment: None, }); // Commands sent to the GPU for drawing during this render pass render_pass.set_pipeline(&pipeline_struct.render_pipeline); render_pass.set_vertex_buffers(0, &[(&pipeline_struct.vertex_buffer, 0)]); render_pass.set_index_buffer(&pipeline_struct.index_buffer, 0); render_pass.set_bind_group(0, &pipeline_struct.texture_bind_group, &[]); render_pass.draw_indexed(0..pipeline_struct.index_count, 0, 0..1); // Done sending render pass commands so we can give up mutation rights to command_encoder drop(render_pass); // Turn the recording of commands into a complete command buffer let command_buffer = command_encoder.finish(); // Submit the command buffer to the GPU command queue self.queue.submit(&[command_buffer]); } } }