//! Low-level workflow: direct use of `Context`, `Renderer`, `PipelineCache`, `Mesh` and //! `Material`, bypassing the `App` facade. Renders a flat quad (shader `standard` **unlit**) via the //! winit 0.30 loop (`EventLoop::run_app` + `ApplicationHandler`). The window and the GPU are created //! in `resumed()`, as required by winit 0.30 and the migration done in `app.rs`. Since Step 8 //! (DRAFT 8.5) the mesh is built via `Mesh::from_geometry(device, Arc, None)` from //! a `Geometry` (positions + colors per vertex) instead of `Mesh::new(device, &[Vertex], ..)`. use std::sync::Arc; use winit::application::ApplicationHandler; use winit::dpi::LogicalSize; use winit::event::WindowEvent; use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop}; use winit::window::{Window, WindowAttributes}; use wsg_lib::core::Context; use wsg_lib::core::Frame; use wsg_lib::core::Renderer; use wsg_lib::pipeline::PipelineCache; use wsg_lib::resources::{Geometry, Material, Mesh}; use wsg_lib::utils; /// Low-level application: holds the GPU objects + window, all created in `resumed`. struct App { /// System window, shared via Arc (as in app.rs). window: Option>, /// GPU context (Instance, Surface, Adapter, Device, Queue). context: Option, /// Execution layer that submits draw calls. renderer: Option, /// Shader/pipeline cache. cache: Option, /// Quad material (pipeline). material: Option, /// Quad mesh (vertices + indices). mesh: Option, } impl ApplicationHandler for App { /// Creates the window then the GPU, and builds the mesh/material. Runs once at startup. /// Redundant `resumed` creating again? Double protection via `self.context.is_some()`. fn resumed(&mut self, event_loop: &ActiveEventLoop) { if self.context.is_some() { return; } event_loop.set_control_flow(ControlFlow::Poll); let attrs = WindowAttributes::default() .with_title("WSG Manual") .with_inner_size(LogicalSize::new(800.0, 600.0)); let window = Arc::new(event_loop.create_window(attrs).unwrap()); // 1. Initialisation let context = pollster::block_on(Context::new(window.clone())).expect("GPU init failed"); // Surface configuration and format retrieval let format = context .configure(&context.adapter, 800, 600) .expect("configuration failed"); // 2. Renderer initialization (it retrieves everything it needs) let device = Arc::new(context.device.clone()); let mut cache = PipelineCache::new(device, context.queue.clone()); cache .register_shader("standard", utils::STANDARD_SHADER_PATH) .unwrap(); // Flat 2D rendering: `standard` in unlit mode (the frame+object bind groups are set by // draw_entity, the default frame matrix is the identity → NDC positions unchanged). let mut renderer = Renderer::new(&context, format, 800, 600); renderer.set_unlit(true); // 3. Material: uses renderer.device() and renderer.format() let material = Material::new(renderer.format(), "standard", &mut cache); // Mesh: uses the renderer's device. Since Step 8 the mesh is built from a // `Geometry` (positions + colors per vertex) via `Mesh::from_geometry` — the mesh also keeps // the `Arc` on the CPU side (retention D5). let geometry = Geometry::new(vec![ // Position (x,y,z) | Color (r,g,b,a) — normals/UVs default via to_vertices [-0.5, 0.5, 0.0], [0.5, 0.5, 0.0], [0.5, -0.5, 0.0], [-0.5, -0.5, 0.0], ]) .with_colors(vec![ [1.0, 0.0, 0.0, 1.0], // top-left (red) [0.0, 1.0, 0.0, 1.0], // top-right (green) [0.0, 0.0, 1.0, 1.0], // bottom-right (blue) [1.0, 1.0, 0.0, 1.0], // bottom-left (yellow) ]) .with_indices(vec![0, 1, 2, 0, 2, 3]); let mesh = Mesh::from_geometry(renderer.device(), Arc::new(geometry), None); self.window = Some(window); self.context = Some(context); self.renderer = Some(renderer); self.cache = Some(cache); self.material = Some(material); self.mesh = Some(mesh); } /// Each frame, requests a redraw for continuous rendering (animation). fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { if let Some(window) = &self.window { window.request_redraw(); } } /// Window event dispatch: RedrawRequested renders then presents, CloseRequested exits. fn window_event( &mut self, event_loop: &ActiveEventLoop, _window_id: winit::window::WindowId, event: WindowEvent, ) { match event { winit::event::WindowEvent::RedrawRequested => { if let (Some(context), Some(renderer), Some(mesh), Some(material)) = (&self.context, &self.renderer, &self.mesh, &self.material) { if let Some(frame) = Frame::try_new(&context.surface) { // 1. Render (no more useless device/queue arguments) renderer.render(frame.view(), mesh, material); // 2. Present renderer.present(frame); } } } winit::event::WindowEvent::CloseRequested => { event_loop.exit(); // this is where you ask the loop to stop } _ => (), } } } fn main() { println!("Current directory: {:?}", std::env::current_dir().unwrap()); let event_loop = EventLoop::new().unwrap(); let mut app = App { window: None, context: None, renderer: None, cache: None, material: None, mesh: None, }; event_loop.run_app(&mut app).unwrap(); }