From 4cd1a3e7cd113768b312cd37bac3db6e9ecf761f Mon Sep 17 00:00:00 2001 From: Jarrod Doyle Date: Thu, 7 Mar 2024 17:01:02 +0000 Subject: [PATCH] Basic window surface --- Cargo.lock | 7 +++ Cargo.toml | 1 + src/gfx/context.rs | 107 +++++++++++++++++++++++++++++++++++++++++++++ src/gfx/mod.rs | 3 ++ src/main.rs | 59 +++++++++++++++++++++---- 5 files changed, 169 insertions(+), 8 deletions(-) create mode 100644 src/gfx/context.rs create mode 100644 src/gfx/mod.rs diff --git a/Cargo.lock b/Cargo.lock index c2a352e..b297686 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1039,6 +1039,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "pollster" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" + [[package]] name = "presser" version = "0.3.1" @@ -1232,6 +1238,7 @@ dependencies = [ "anyhow", "env_logger", "log", + "pollster", "wgpu", "winit", ] diff --git a/Cargo.toml b/Cargo.toml index 472900f..e7a4b8a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,5 +9,6 @@ edition = "2021" anyhow = "1.0.80" env_logger = "0.11.3" log = "0.4.21" +pollster = "0.3.0" wgpu = "0.19.3" winit = "0.29.14" diff --git a/src/gfx/context.rs b/src/gfx/context.rs new file mode 100644 index 0000000..2b67363 --- /dev/null +++ b/src/gfx/context.rs @@ -0,0 +1,107 @@ +use anyhow::Result; +use winit::{ + dpi::PhysicalSize, event::WindowEvent, event_loop::EventLoopWindowTarget, window::Window, +}; + +pub struct Context<'w> { + pub window: &'w Window, + pub instance: wgpu::Instance, + pub size: PhysicalSize, + pub surface: wgpu::Surface<'w>, + pub surface_config: wgpu::SurfaceConfiguration, + pub adapter: wgpu::Adapter, + pub device: wgpu::Device, + pub queue: wgpu::Queue, +} + +impl<'w> Context<'w> { + pub async fn new(window: &'w Window, limits: wgpu::Limits) -> Result { + log::info!("Initialising WGPU context..."); + let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { + backends: wgpu::Backends::VULKAN, + dx12_shader_compiler: Default::default(), + ..Default::default() + }); + + // To be able to start drawing we need a few things: + // - A surface + // - A GPU device to draw to the surface + // - A draw command queue + log::info!("Initialising window surface..."); + let surface = instance.create_surface(window)?; + + log::info!("Requesting GPU adapter..."); + let adapter = instance + .request_adapter(&wgpu::RequestAdapterOptions { + power_preference: wgpu::PowerPreference::HighPerformance, + force_fallback_adapter: false, + compatible_surface: Some(&surface), + }) + .await + .unwrap(); + + log::info!("Checking GPU adapter meets requirements"); + log::info!("Requesting GPU device..."); + let (device, queue) = adapter + .request_device( + &wgpu::DeviceDescriptor { + label: None, + required_features: wgpu::Features::empty(), + required_limits: limits, + }, + None, + ) + .await + .unwrap(); + + log::info!("Configuring window surface..."); + let size = window.inner_size(); + let surface_config = surface + .get_default_config(&adapter, size.width, size.height) + .unwrap(); + surface.configure(&device, &surface_config); + + Ok(Self { + window, + instance, + size, + surface, + surface_config, + adapter, + device, + queue, + }) + } + + pub fn resize_surface(&mut self, new_size: PhysicalSize) { + if new_size.width > 0 && new_size.height > 0 { + self.size = new_size; + self.surface_config.width = new_size.width; + self.surface_config.height = new_size.height; + self.surface.configure(&self.device, &self.surface_config); + } + } + + pub fn handle_window_event( + &mut self, + event: &WindowEvent, + elwt: &EventLoopWindowTarget<()>, + ) -> bool { + let mut handled = true; + match event { + WindowEvent::CloseRequested => { + elwt.exit(); + } + WindowEvent::Resized(physical_size) => { + self.resize_surface(*physical_size); + } + WindowEvent::ScaleFactorChanged { .. } => { + self.resize_surface(self.window.inner_size()); + } + + _ => handled = false, + } + + handled + } +} diff --git a/src/gfx/mod.rs b/src/gfx/mod.rs new file mode 100644 index 0000000..1e0bcf4 --- /dev/null +++ b/src/gfx/mod.rs @@ -0,0 +1,3 @@ +mod context; + +pub use self::context::Context; diff --git a/src/main.rs b/src/main.rs index a5059fb..11c07ef 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,12 @@ +mod gfx; + use anyhow::Result; +use gfx::Context; +use wgpu::Limits; use winit::{ dpi::LogicalSize, event::*, - event_loop::{ControlFlow, EventLoop, EventLoopWindowTarget}, + event_loop::{ControlFlow, EventLoop}, window::{Window, WindowBuilder}, }; @@ -10,15 +14,23 @@ pub fn main() -> Result<()> { env_logger::init(); let (event_loop, window) = make_window()?; - run(event_loop, window)?; + let context = pollster::block_on(Context::new(&window, Limits::default()))?; + run(event_loop, context)?; Ok(()) } -pub fn run(event_loop: EventLoop<()>, window: Window) -> Result<()> { +pub fn run(event_loop: EventLoop<()>, mut context: Context) -> Result<()> { event_loop.run(|event, elwt| match event { - Event::WindowEvent { window_id, event } if window_id == window.id() => { - handle_window_event(event, elwt); + Event::WindowEvent { window_id, event } if window_id == context.window.id() => { + if context.handle_window_event(&event, elwt) { + return; + } + + if let WindowEvent::RedrawRequested = event { + render(&context); + context.window.request_redraw(); + } } _ => (), })?; @@ -38,8 +50,39 @@ fn make_window() -> Result<(EventLoop<()>, Window)> { Ok((event_loop, window)) } -fn handle_window_event(event: WindowEvent, elwt: &EventLoopWindowTarget<()>) { - if let WindowEvent::CloseRequested = event { - elwt.exit(); +fn render(context: &Context) { + let output = context.surface.get_current_texture().unwrap(); + let view = output + .texture + .create_view(&wgpu::TextureViewDescriptor::default()); + let mut encoder = context + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("Render Encoder"), + }); + { + let _render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("Render Pass"), + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color { + r: 0.1, + g: 0.2, + b: 0.3, + a: 1.0, + }), + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: None, + occlusion_query_set: None, + timestamp_writes: None, + }); } + + // submit will accept anything that implements IntoIter + context.queue.submit(std::iter::once(encoder.finish())); + output.present(); }