# HG changeset patch # User alfadur # Date 1611184319 -10800 # Node ID ec85fdf82942000debf55fca2409afb6fbda7684 # Parent 95402fa4e1913375f8fdb22e8e11eb6686607f4c add wgpu support to hwrunner diff -r 95402fa4e191 -r ec85fdf82942 rust/hwrunner/Cargo.toml --- a/rust/hwrunner/Cargo.toml Thu Jan 07 17:13:32 2021 +0300 +++ b/rust/hwrunner/Cargo.toml Thu Jan 21 02:11:59 2021 +0300 @@ -7,6 +7,8 @@ [dependencies] glutin = "0.20" gl = "0.11" +futures = "0.3" +wgpu = "0.6" integral-geometry = { path = "../integral-geometry" } lib-hedgewars-engine = { path = "../lib-hedgewars-engine" } diff -r 95402fa4e191 -r ec85fdf82942 rust/hwrunner/src/main.rs --- a/rust/hwrunner/src/main.rs Thu Jan 07 17:13:32 2021 +0300 +++ b/rust/hwrunner/src/main.rs Thu Jan 21 02:11:59 2021 +0300 @@ -1,69 +1,214 @@ +use std::time::Duration; + +use futures::executor::block_on; use glutin::{ dpi, ContextTrait, DeviceEvent, ElementState, Event, EventsLoop, GlProfile, GlRequest, - MouseButton, MouseScrollDelta, WindowEvent, WindowedContext, + MouseButton, MouseScrollDelta, Window, WindowBuilder, WindowEvent, WindowedContext, +}; +use hedgewars_engine::instance::EngineInstance; +use integral_geometry::Point; +use std::error::Error; +use wgpu::{ + Adapter, BackendBit, Color, CommandEncoderDescriptor, Device, DeviceDescriptor, Features, + LoadOp, Operations, PowerPreference, PresentMode, Queue, RenderPassColorAttachmentDescriptor, + RenderPassDescriptor, RequestAdapterOptions, Surface, SwapChain, SwapChainDescriptor, + TextureFormat, TextureUsage, }; -use hedgewars_engine::instance::EngineInstance; +type HwGlRendererContext = WindowedContext; + +struct HwWgpuRenderingContext { + window: Window, + surface: Surface, + adapter: Adapter, + device: Device, + queue: Queue, + swap_chain: SwapChain, +} + +enum HwRendererContext { + Gl(HwGlRendererContext), + Wgpu(HwWgpuRenderingContext), +} + +struct ErrorStub; + +impl From for ErrorStub { + fn from(_: T) -> Self { + ErrorStub + } +} -use integral_geometry::Point; -use std::time::Duration; +impl HwRendererContext { + pub fn window(&self) -> &Window { + match self { + HwRendererContext::Gl(gl) => &gl.window(), + HwRendererContext::Wgpu(wgpu) => &wgpu.window, + } + } + + pub fn update(&mut self, size: dpi::LogicalSize) { + let phys = size.to_physical(self.window().get_hidpi_factor()); + match self { + HwRendererContext::Gl(context) => unsafe { + gl::Viewport(0, 0, phys.width as i32, phys.height as i32); + }, + HwRendererContext::Wgpu(context) => { + context.swap_chain = context.device.create_swap_chain( + &context.surface, + &SwapChainDescriptor { + usage: TextureUsage::OUTPUT_ATTACHMENT, + format: TextureFormat::Bgra8Unorm, + width: phys.width as u32, + height: phys.height as u32, + present_mode: PresentMode::Fifo, + }, + ); + } + } + } + + pub fn present(&mut self) -> Result<(), ErrorStub> { + match self { + HwRendererContext::Gl(context) => context.swap_buffers()?, + HwRendererContext::Wgpu(context) => { + let frame_view = &context.swap_chain.get_current_frame()?.output.view; -fn init(event_loop: &EventsLoop, size: dpi::LogicalSize) -> WindowedContext { - use glutin::{ContextBuilder, WindowBuilder}; + let mut encoder = + context + .device + .create_command_encoder(&CommandEncoderDescriptor { + label: Some("Main encoder"), + }); + encoder.begin_render_pass(&RenderPassDescriptor { + color_attachments: &[RenderPassColorAttachmentDescriptor { + attachment: &frame_view, + resolve_target: None, + ops: Operations { + load: LoadOp::Clear(Color::BLUE), + store: false, + }, + }], + depth_stencil_attachment: None, + }); + let buffer = encoder.finish(); + context.queue.submit(std::iter::once(buffer)); + } + } + Ok(()) + } +} + +fn init_wgpu(event_loop: &EventsLoop, size: dpi::LogicalSize) -> HwWgpuRenderingContext { + let builder = WindowBuilder::new() + .with_title("hwengine") + .with_dimensions(size); + let window = builder.build(event_loop).unwrap(); + + let instance = wgpu::Instance::new(BackendBit::PRIMARY); + + let surface = unsafe { instance.create_surface(&window) }; - let window = WindowBuilder::new() + let adapter = block_on(instance.request_adapter(&RequestAdapterOptions { + power_preference: PowerPreference::HighPerformance, + compatible_surface: Some(&surface), + })) + .unwrap(); + + let (device, queue) = block_on(adapter.request_device(&Default::default(), None)).unwrap(); + + let size = window.get_inner_size().unwrap(); + + let phys = size.to_physical(window.get_hidpi_factor()); + + let mut swap_chain = device.create_swap_chain( + &surface, + &SwapChainDescriptor { + usage: TextureUsage::OUTPUT_ATTACHMENT, + format: TextureFormat::Bgra8Unorm, + width: phys.width as u32, + height: phys.height as u32, + present_mode: PresentMode::Fifo, + }, + ); + + HwWgpuRenderingContext { + window, + surface, + adapter, + device, + queue, + swap_chain, + } +} + +fn init_gl(event_loop: &EventsLoop, size: dpi::LogicalSize) -> HwGlRendererContext { + use glutin::ContextBuilder; + + let builder = WindowBuilder::new() .with_title("hwengine") .with_dimensions(size); - let cxt = ContextBuilder::new() + let context = ContextBuilder::new() .with_gl(GlRequest::Latest) .with_gl_profile(GlProfile::Core) - .build_windowed(window, &event_loop) + .build_windowed(builder, &event_loop) .ok() .unwrap(); unsafe { - cxt.make_current().unwrap(); - gl::load_with(|ptr| cxt.get_proc_address(ptr) as *const _); + context.make_current().unwrap(); + gl::load_with(|ptr| context.get_proc_address(ptr) as *const _); - if let Some(sz) = cxt.get_inner_size() { - let phys = sz.to_physical(cxt.get_hidpi_factor()); + if let Some(sz) = context.get_inner_size() { + let phys = sz.to_physical(context.get_hidpi_factor()); gl::Viewport(0, 0, phys.width as i32, phys.height as i32); } } - cxt + context +} + +fn init(event_loop: &EventsLoop, size: dpi::LogicalSize, use_wgpu: bool) -> HwRendererContext { + if use_wgpu { + HwRendererContext::Wgpu(init_wgpu(event_loop, size)) + } else { + HwRendererContext::Gl(init_gl(event_loop, size)) + } } fn main() { + let use_wgpu = false; let mut event_loop = EventsLoop::new(); let (w, h) = (1024.0, 768.0); - let window = init(&event_loop, dpi::LogicalSize::new(w, h)); + + let mut context = init(&event_loop, dpi::LogicalSize::new(w, h), use_wgpu); let mut engine = EngineInstance::new(); - engine.world.create_renderer(w as u16, h as u16); + if !use_wgpu { + engine.world.create_renderer(w as u16, h as u16); + } let mut dragging = false; use std::time::Instant; let mut now = Instant::now(); - let mut update = Instant::now(); - let mut render = Instant::now(); + let mut update_time = Instant::now(); + let mut render_time = Instant::now(); - unsafe { window.make_current().unwrap() }; let mut is_running = true; while is_running { - let curr = Instant::now(); - let delta = curr - now; - now = curr; + let current_time = Instant::now(); + let delta = current_time - now; + now = current_time; let ms = delta.as_secs() as f64 * 1000.0 + delta.subsec_millis() as f64; - window.set_title(&format!("hwengine {:.3}ms", ms)); + context.window().set_title(&format!("hwengine {:.3}ms", ms)); - if update.elapsed() > Duration::from_millis(10) { - update = curr; + if update_time.elapsed() > Duration::from_millis(10) { + update_time = current_time; engine.world.step() } @@ -72,6 +217,8 @@ WindowEvent::CloseRequested => { is_running = false; } + WindowEvent::Resized(size) => context.update(size), + WindowEvent::MouseInput { button, state, .. } => { if let MouseButton::Right = button { dragging = state == ElementState::Pressed; @@ -82,7 +229,7 @@ let zoom_change = match delta { MouseScrollDelta::LineDelta(x, y) => y as f32 * 0.1f32, MouseScrollDelta::PixelDelta(delta) => { - let physical = delta.to_physical(window.get_hidpi_factor()); + let physical = delta.to_physical(context.window().get_hidpi_factor()); physical.y as f32 * 0.1f32 } }; @@ -103,10 +250,12 @@ _ => (), }); - if render.elapsed() > Duration::from_millis(16) { - render = curr; - engine.render(); - window.swap_buffers().unwrap(); + if render_time.elapsed() > Duration::from_millis(16) { + render_time = current_time; + if !use_wgpu { + engine.render(); + } + context.present(); } } }