From 719d69a1ee359637ea74a200f47257ca2284dee8 Mon Sep 17 00:00:00 2001 From: Billy Messenger <60663878+BillyDM@users.noreply.github.com> Date: Thu, 25 Jul 2024 13:05:55 -0500 Subject: [PATCH] update example to use winit 0.30 (#105) * update example to use winit 0.30 * omit unneeded field in example --- Cargo.toml | 4 +- examples/hello-world.rs | 358 +++++++++++++++++++++++----------------- 2 files changed, 211 insertions(+), 151 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 47048df..b87a8e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,6 @@ lru = { version = "0.12.1", default-features = false } rustc-hash = "2.0" [dev-dependencies] -winit = { version = "0.29.10", features = ["rwh_05"] } -wgpu = { version = "22", default-features = true } +winit = "0.30.3" +wgpu = "22" pollster = "0.3.0" diff --git a/examples/hello-world.rs b/examples/hello-world.rs index b1cfabe..06ce896 100644 --- a/examples/hello-world.rs +++ b/examples/hello-world.rs @@ -9,158 +9,218 @@ use wgpu::{ RenderPassDescriptor, RequestAdapterOptions, SurfaceConfiguration, TextureFormat, TextureUsages, TextureViewDescriptor, }; -use winit::{ - dpi::LogicalSize, - event::{Event, WindowEvent}, - event_loop::EventLoop, - window::WindowBuilder, -}; +use winit::{dpi::LogicalSize, event::WindowEvent, event_loop::EventLoop, window::Window}; fn main() { - pollster::block_on(run()); -} - -async fn run() { - // Set up window - let (width, height) = (800, 600); let event_loop = EventLoop::new().unwrap(); - let window = Arc::new( - WindowBuilder::new() - .with_inner_size(LogicalSize::new(width as f64, height as f64)) - .with_title("glyphon hello world") - .build(&event_loop) - .unwrap(), - ); - let size = window.inner_size(); - let scale_factor = window.scale_factor(); - - // Set up surface - let instance = Instance::new(InstanceDescriptor::default()); - let adapter = instance - .request_adapter(&RequestAdapterOptions::default()) - .await - .unwrap(); - let (device, queue) = adapter - .request_device(&DeviceDescriptor::default(), None) - .await - .unwrap(); - - let surface = instance - .create_surface(window.clone()) - .expect("Create surface"); - let swapchain_format = TextureFormat::Bgra8UnormSrgb; - let mut config = SurfaceConfiguration { - usage: TextureUsages::RENDER_ATTACHMENT, - format: swapchain_format, - width: size.width, - height: size.height, - present_mode: PresentMode::Fifo, - alpha_mode: CompositeAlphaMode::Opaque, - view_formats: vec![], - desired_maximum_frame_latency: 2, - }; - surface.configure(&device, &config); - - // Set up text renderer - let mut font_system = FontSystem::new(); - let mut swash_cache = SwashCache::new(); - let cache = Cache::new(&device); - let mut viewport = Viewport::new(&device, &cache); - let mut atlas = TextAtlas::new(&device, &queue, &cache, swapchain_format); - let mut text_renderer = - TextRenderer::new(&mut atlas, &device, MultisampleState::default(), None); - let mut buffer = Buffer::new(&mut font_system, Metrics::new(30.0, 42.0)); - - let physical_width = (width as f64 * scale_factor) as f32; - let physical_height = (height as f64 * scale_factor) as f32; - - buffer.set_size( - &mut font_system, - Some(physical_width), - Some(physical_height), - ); - buffer.set_text(&mut font_system, "Hello world! 👋\nThis is rendered with 🦅 glyphon 🦁\nThe text below should be partially clipped.\na b c d e f g h i j k l m n o p q r s t u v w x y z", Attrs::new().family(Family::SansSerif), Shaping::Advanced); - buffer.shape_until_scroll(&mut font_system, false); - event_loop - .run(move |event, target| { - if let Event::WindowEvent { - window_id: _, - event, - } = event - { - match event { - WindowEvent::Resized(size) => { - config.width = size.width; - config.height = size.height; - surface.configure(&device, &config); - window.request_redraw(); - } - WindowEvent::RedrawRequested => { - viewport.update( - &queue, - Resolution { - width: config.width, - height: config.height, - }, - ); - - text_renderer - .prepare( - &device, - &queue, - &mut font_system, - &mut atlas, - &viewport, - [TextArea { - buffer: &buffer, - left: 10.0, - top: 10.0, - scale: 1.0, - bounds: TextBounds { - left: 0, - top: 0, - right: 600, - bottom: 160, - }, - default_color: Color::rgb(255, 255, 255), - }], - &mut swash_cache, - ) - .unwrap(); - - let frame = surface.get_current_texture().unwrap(); - let view = frame.texture.create_view(&TextureViewDescriptor::default()); - let mut encoder = device - .create_command_encoder(&CommandEncoderDescriptor { label: None }); - { - let mut pass = encoder.begin_render_pass(&RenderPassDescriptor { - label: None, - color_attachments: &[Some(RenderPassColorAttachment { - view: &view, - resolve_target: None, - ops: Operations { - load: LoadOp::Clear(wgpu::Color::BLACK), - store: wgpu::StoreOp::Store, - }, - })], - depth_stencil_attachment: None, - timestamp_writes: None, - occlusion_query_set: None, - }); - - text_renderer.render(&atlas, &viewport, &mut pass).unwrap(); - } - - queue.submit(Some(encoder.finish())); - frame.present(); - - atlas.trim(); - } - WindowEvent::CloseRequested => target.exit(), - _ => {} - } - } - }) + .run_app(&mut Application { window_state: None }) .unwrap(); } + +struct WindowState { + device: wgpu::Device, + queue: wgpu::Queue, + surface: wgpu::Surface<'static>, + surface_config: SurfaceConfiguration, + + font_system: FontSystem, + swash_cache: SwashCache, + viewport: glyphon::Viewport, + atlas: glyphon::TextAtlas, + text_renderer: glyphon::TextRenderer, + text_buffer: glyphon::Buffer, + + // Make sure that the winit window is last in the struct so that + // it is dropped after the wgpu surface is dropped, otherwise the + // program may crash when closed. This is probably a bug in wgpu. + window: Arc, +} + +impl WindowState { + async fn new(window: Arc) -> Self { + let physical_size = window.inner_size(); + let scale_factor = window.scale_factor(); + + // Set up surface + let instance = Instance::new(InstanceDescriptor::default()); + let adapter = instance + .request_adapter(&RequestAdapterOptions::default()) + .await + .unwrap(); + let (device, queue) = adapter + .request_device(&DeviceDescriptor::default(), None) + .await + .unwrap(); + + let surface = instance + .create_surface(window.clone()) + .expect("Create surface"); + let swapchain_format = TextureFormat::Bgra8UnormSrgb; + let surface_config = SurfaceConfiguration { + usage: TextureUsages::RENDER_ATTACHMENT, + format: swapchain_format, + width: physical_size.width, + height: physical_size.height, + present_mode: PresentMode::Fifo, + alpha_mode: CompositeAlphaMode::Opaque, + view_formats: vec![], + desired_maximum_frame_latency: 2, + }; + surface.configure(&device, &surface_config); + + // Set up text renderer + let mut font_system = FontSystem::new(); + let swash_cache = SwashCache::new(); + let cache = Cache::new(&device); + let viewport = Viewport::new(&device, &cache); + let mut atlas = TextAtlas::new(&device, &queue, &cache, swapchain_format); + let text_renderer = + TextRenderer::new(&mut atlas, &device, MultisampleState::default(), None); + let mut text_buffer = Buffer::new(&mut font_system, Metrics::new(30.0, 42.0)); + + let physical_width = (physical_size.width as f64 * scale_factor) as f32; + let physical_height = (physical_size.height as f64 * scale_factor) as f32; + + text_buffer.set_size( + &mut font_system, + Some(physical_width), + Some(physical_height), + ); + text_buffer.set_text(&mut font_system, "Hello world! 👋\nThis is rendered with 🦅 glyphon 🦁\nThe text below should be partially clipped.\na b c d e f g h i j k l m n o p q r s t u v w x y z", Attrs::new().family(Family::SansSerif), Shaping::Advanced); + text_buffer.shape_until_scroll(&mut font_system, false); + + Self { + device, + queue, + surface, + surface_config, + font_system, + swash_cache, + viewport, + atlas, + text_renderer, + text_buffer, + window, + } + } +} + +struct Application { + window_state: Option, +} + +impl winit::application::ApplicationHandler for Application { + fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) { + if self.window_state.is_some() { + return; + } + + // Set up window + let (width, height) = (800, 600); + let window_attributes = Window::default_attributes() + .with_inner_size(LogicalSize::new(width as f64, height as f64)) + .with_title("glyphon hello world"); + let window = Arc::new(event_loop.create_window(window_attributes).unwrap()); + + self.window_state = Some(pollster::block_on(WindowState::new(window))); + } + + fn window_event( + &mut self, + event_loop: &winit::event_loop::ActiveEventLoop, + _window_id: winit::window::WindowId, + event: WindowEvent, + ) { + let Some(state) = &mut self.window_state else { + return; + }; + + let WindowState { + window, + device, + queue, + surface, + surface_config, + font_system, + swash_cache, + viewport, + atlas, + text_renderer, + text_buffer, + .. + } = state; + + match event { + WindowEvent::Resized(size) => { + surface_config.width = size.width; + surface_config.height = size.height; + surface.configure(&device, &surface_config); + window.request_redraw(); + } + WindowEvent::RedrawRequested => { + viewport.update( + &queue, + Resolution { + width: surface_config.width, + height: surface_config.height, + }, + ); + + text_renderer + .prepare( + device, + queue, + font_system, + atlas, + viewport, + [TextArea { + buffer: text_buffer, + left: 10.0, + top: 10.0, + scale: 1.0, + bounds: TextBounds { + left: 0, + top: 0, + right: 600, + bottom: 160, + }, + default_color: Color::rgb(255, 255, 255), + }], + swash_cache, + ) + .unwrap(); + + let frame = surface.get_current_texture().unwrap(); + let view = frame.texture.create_view(&TextureViewDescriptor::default()); + let mut encoder = + device.create_command_encoder(&CommandEncoderDescriptor { label: None }); + { + let mut pass = encoder.begin_render_pass(&RenderPassDescriptor { + label: None, + color_attachments: &[Some(RenderPassColorAttachment { + view: &view, + resolve_target: None, + ops: Operations { + load: LoadOp::Clear(wgpu::Color::BLACK), + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: None, + timestamp_writes: None, + occlusion_query_set: None, + }); + + text_renderer.render(&atlas, &viewport, &mut pass).unwrap(); + } + + queue.submit(Some(encoder.finish())); + frame.present(); + + atlas.trim(); + } + WindowEvent::CloseRequested => event_loop.exit(), + _ => {} + } + } +}