add wgpu support to hwrunner
authoralfadur
Thu, 21 Jan 2021 02:11:59 +0300
changeset 15776 ec85fdf82942
parent 15775 95402fa4e191
child 15777 2b3f8af6f647
add wgpu support to hwrunner
rust/hwrunner/Cargo.toml
rust/hwrunner/src/main.rs
--- 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" }
--- 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<T: Error> From<T> 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();
         }
     }
 }