From f1e20a61f16388ed4d2dac734bab30d67bbd84b3 Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector0193@gmail.com>
Date: Sun, 9 Feb 2020 03:25:13 +0100
Subject: Allow `iced_wgpu` to render to any `TextureView`

---
 wgpu/src/lib.rs               |  10 +++-
 wgpu/src/renderer.rs          | 116 ++++++++++++------------------------------
 wgpu/src/renderer/target.rs   |  91 ---------------------------------
 wgpu/src/target.rs            |   8 +++
 wgpu/src/viewport.rs          |  32 ++++++++++++
 wgpu/src/window.rs            |   5 ++
 wgpu/src/window/backend.rs    |  99 +++++++++++++++++++++++++++++++++++
 wgpu/src/window/swap_chain.rs |  49 ++++++++++++++++++
 8 files changed, 233 insertions(+), 177 deletions(-)
 delete mode 100644 wgpu/src/renderer/target.rs
 create mode 100644 wgpu/src/target.rs
 create mode 100644 wgpu/src/viewport.rs
 create mode 100644 wgpu/src/window.rs
 create mode 100644 wgpu/src/window/backend.rs
 create mode 100644 wgpu/src/window/swap_chain.rs

(limited to 'wgpu')

diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs
index b8cd3fce..9c1739b2 100644
--- a/wgpu/src/lib.rs
+++ b/wgpu/src/lib.rs
@@ -19,7 +19,7 @@
 //! [`wgpu`]: https://github.com/gfx-rs/wgpu-rs
 //! [WebGPU API]: https://gpuweb.github.io/gpuweb/
 //! [`wgpu_glyph`]: https://github.com/hecrj/wgpu_glyph
-#![deny(missing_docs)]
+//#![deny(missing_docs)]
 #![deny(missing_debug_implementations)]
 #![deny(unused_results)]
 #![forbid(unsafe_code)]
@@ -27,19 +27,25 @@
 pub mod defaults;
 pub mod triangle;
 pub mod widget;
+pub mod window;
 
 mod image;
 mod primitive;
 mod quad;
 mod renderer;
 mod settings;
+mod target;
 mod text;
 mod transformation;
+mod viewport;
 
 pub use defaults::Defaults;
 pub use primitive::Primitive;
-pub use renderer::{Renderer, Target};
+pub use renderer::Renderer;
 pub use settings::Settings;
+pub use target::Target;
+pub use viewport::Viewport;
+
 #[doc(no_inline)]
 pub use widget::*;
 
diff --git a/wgpu/src/renderer.rs b/wgpu/src/renderer.rs
index 93d2bb13..a99080f4 100644
--- a/wgpu/src/renderer.rs
+++ b/wgpu/src/renderer.rs
@@ -1,29 +1,20 @@
 use crate::{
     image, quad, text, triangle, Defaults, Image, Primitive, Quad, Settings,
-    Transformation,
+    Target, Transformation,
 };
 use iced_native::{
-    layout, window, Background, Color, Layout, MouseCursor, Point, Rectangle,
-    Vector, Widget,
+    layout, Background, Color, Layout, MouseCursor, Point, Rectangle, Vector,
+    Widget,
 };
 use std::sync::Arc;
-use wgpu::{
-    Adapter, BackendBit, CommandEncoderDescriptor, Device, DeviceDescriptor,
-    Extensions, Limits, PowerPreference, Queue, RequestAdapterOptions,
-};
 
-mod target;
 mod widget;
 
-pub use target::Target;
-
 /// A [`wgpu`] renderer.
 ///
 /// [`wgpu`]: https://github.com/gfx-rs/wgpu-rs
 #[derive(Debug)]
 pub struct Renderer {
-    device: Device,
-    queue: Queue,
     quad_pipeline: quad::Pipeline,
     image_pipeline: image::Pipeline,
     text_pipeline: text::Pipeline,
@@ -53,29 +44,16 @@ impl<'a> Layer<'a> {
 }
 
 impl Renderer {
-    fn new(settings: Settings) -> Self {
-        let adapter = Adapter::request(&RequestAdapterOptions {
-            power_preference: PowerPreference::Default,
-            backends: BackendBit::all(),
-        })
-        .expect("Request adapter");
-
-        let (mut device, queue) = adapter.request_device(&DeviceDescriptor {
-            extensions: Extensions {
-                anisotropic_filtering: false,
-            },
-            limits: Limits { max_bind_groups: 2 },
-        });
-
-        let text_pipeline =
-            text::Pipeline::new(&mut device, settings.default_font);
-        let quad_pipeline = quad::Pipeline::new(&mut device);
-        let image_pipeline = crate::image::Pipeline::new(&mut device);
-        let triangle_pipeline = triangle::Pipeline::new(&mut device);
+    /// Creates a new [`Renderer`].
+    ///
+    /// [`Renderer`]: struct.Renderer.html
+    pub fn new(settings: Settings, device: &mut wgpu::Device) -> Self {
+        let text_pipeline = text::Pipeline::new(device, settings.default_font);
+        let quad_pipeline = quad::Pipeline::new(device);
+        let image_pipeline = crate::image::Pipeline::new(device);
+        let triangle_pipeline = triangle::Pipeline::new(device);
 
         Self {
-            device,
-            queue,
             quad_pipeline,
             image_pipeline,
             text_pipeline,
@@ -83,38 +61,25 @@ impl Renderer {
         }
     }
 
-    fn draw<T: AsRef<str>>(
+    /// Draws the provided primitives in the given [`Target`].
+    ///
+    /// The text provided as overlay will be renderer on top of the primitives.
+    /// This is useful for rendering debug information.
+    ///
+    /// [`Target`]: struct.Target.html
+    pub fn draw<T: AsRef<str>>(
         &mut self,
+        device: &mut wgpu::Device,
+        encoder: &mut wgpu::CommandEncoder,
+        target: Target<'_>,
         (primitive, mouse_cursor): &(Primitive, MouseCursor),
         overlay: &[T],
-        target: &mut Target,
     ) -> MouseCursor {
         log::debug!("Drawing");
 
-        let (width, height) = target.dimensions();
-        let scale_factor = target.scale_factor();
-        let transformation = target.transformation();
-        let frame = target.next_frame();
-
-        let mut encoder = self
-            .device
-            .create_command_encoder(&CommandEncoderDescriptor { todo: 0 });
-
-        let _ = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
-            color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
-                attachment: &frame.view,
-                resolve_target: None,
-                load_op: wgpu::LoadOp::Clear,
-                store_op: wgpu::StoreOp::Store,
-                clear_color: wgpu::Color {
-                    r: 1.0,
-                    g: 1.0,
-                    b: 1.0,
-                    a: 1.0,
-                },
-            }],
-            depth_stencil_attachment: None,
-        });
+        let (width, height) = target.viewport.dimensions();
+        let scale_factor = target.viewport.scale_factor();
+        let transformation = target.viewport.transformation();
 
         let mut layers = Vec::new();
 
@@ -133,15 +98,15 @@ impl Renderer {
 
         for layer in layers {
             self.flush(
+                device,
                 scale_factor,
                 transformation,
                 &layer,
-                &mut encoder,
-                &frame.view,
+                encoder,
+                target.texture,
             );
         }
 
-        self.queue.submit(&[encoder.finish()]);
         self.image_pipeline.trim_cache();
 
         *mouse_cursor
@@ -336,6 +301,7 @@ impl Renderer {
 
     fn flush(
         &mut self,
+        device: &mut wgpu::Device,
         scale_factor: f32,
         transformation: Transformation,
         layer: &Layer<'_>,
@@ -352,7 +318,7 @@ impl Renderer {
                 );
 
             self.triangle_pipeline.draw(
-                &mut self.device,
+                device,
                 encoder,
                 target,
                 translated,
@@ -364,7 +330,7 @@ impl Renderer {
 
         if layer.quads.len() > 0 {
             self.quad_pipeline.draw(
-                &mut self.device,
+                device,
                 encoder,
                 &layer.quads,
                 transformation,
@@ -383,7 +349,7 @@ impl Renderer {
                 );
 
             self.image_pipeline.draw(
-                &mut self.device,
+                device,
                 encoder,
                 &layer.images,
                 translated_and_scaled,
@@ -429,7 +395,7 @@ impl Renderer {
             }
 
             self.text_pipeline.draw_queued(
-                &mut self.device,
+                device,
                 encoder,
                 target,
                 transformation,
@@ -461,24 +427,6 @@ impl iced_native::Renderer for Renderer {
     }
 }
 
-impl window::Renderer for Renderer {
-    type Settings = Settings;
-    type Target = Target;
-
-    fn new(settings: Settings) -> Self {
-        Self::new(settings)
-    }
-
-    fn draw<T: AsRef<str>>(
-        &mut self,
-        output: &Self::Output,
-        overlay: &[T],
-        target: &mut Target,
-    ) -> MouseCursor {
-        self.draw(output, overlay, target)
-    }
-}
-
 impl layout::Debugger for Renderer {
     fn explain<Message>(
         &mut self,
diff --git a/wgpu/src/renderer/target.rs b/wgpu/src/renderer/target.rs
deleted file mode 100644
index 20974976..00000000
--- a/wgpu/src/renderer/target.rs
+++ /dev/null
@@ -1,91 +0,0 @@
-use crate::{Renderer, Transformation};
-use iced_native::window;
-
-use raw_window_handle::HasRawWindowHandle;
-
-/// A rendering target.
-#[derive(Debug)]
-pub struct Target {
-    surface: wgpu::Surface,
-    width: u32,
-    height: u32,
-    scale_factor: f32,
-    transformation: Transformation,
-    swap_chain: wgpu::SwapChain,
-}
-
-impl Target {
-    pub(crate) fn dimensions(&self) -> (u32, u32) {
-        (self.width, self.height)
-    }
-
-    pub(crate) fn scale_factor(&self) -> f32 {
-        self.scale_factor
-    }
-
-    pub(crate) fn transformation(&self) -> Transformation {
-        self.transformation
-    }
-
-    pub(crate) fn next_frame(&mut self) -> wgpu::SwapChainOutput<'_> {
-        self.swap_chain.get_next_texture()
-    }
-}
-
-impl window::Target for Target {
-    type Renderer = Renderer;
-
-    fn new<W: HasRawWindowHandle>(
-        window: &W,
-        width: u32,
-        height: u32,
-        scale_factor: f64,
-        renderer: &Renderer,
-    ) -> Target {
-        let surface = wgpu::Surface::create(window);
-        let swap_chain =
-            new_swap_chain(&surface, width, height, &renderer.device);
-
-        Target {
-            surface,
-            width,
-            height,
-            scale_factor: scale_factor as f32,
-            transformation: Transformation::orthographic(width, height),
-            swap_chain,
-        }
-    }
-
-    fn resize(
-        &mut self,
-        width: u32,
-        height: u32,
-        scale_factor: f64,
-        renderer: &Renderer,
-    ) {
-        self.width = width;
-        self.height = height;
-        self.scale_factor = scale_factor as f32;
-        self.transformation = Transformation::orthographic(width, height);
-        self.swap_chain =
-            new_swap_chain(&self.surface, width, height, &renderer.device);
-    }
-}
-
-fn new_swap_chain(
-    surface: &wgpu::Surface,
-    width: u32,
-    height: u32,
-    device: &wgpu::Device,
-) -> wgpu::SwapChain {
-    device.create_swap_chain(
-        &surface,
-        &wgpu::SwapChainDescriptor {
-            usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
-            format: wgpu::TextureFormat::Bgra8UnormSrgb,
-            width,
-            height,
-            present_mode: wgpu::PresentMode::Vsync,
-        },
-    )
-}
diff --git a/wgpu/src/target.rs b/wgpu/src/target.rs
new file mode 100644
index 00000000..544e83d1
--- /dev/null
+++ b/wgpu/src/target.rs
@@ -0,0 +1,8 @@
+use crate::Viewport;
+
+/// A rendering target.
+#[derive(Debug)]
+pub struct Target<'a> {
+    pub texture: &'a wgpu::TextureView,
+    pub viewport: &'a Viewport,
+}
diff --git a/wgpu/src/viewport.rs b/wgpu/src/viewport.rs
new file mode 100644
index 00000000..a4bcb704
--- /dev/null
+++ b/wgpu/src/viewport.rs
@@ -0,0 +1,32 @@
+use crate::Transformation;
+
+#[derive(Debug)]
+pub struct Viewport {
+    width: u32,
+    height: u32,
+    scale_factor: f32,
+    transformation: Transformation,
+}
+
+impl Viewport {
+    pub fn new(width: u32, height: u32, scale_factor: f64) -> Viewport {
+        Viewport {
+            width,
+            height,
+            scale_factor: scale_factor as f32,
+            transformation: Transformation::orthographic(width, height),
+        }
+    }
+
+    pub fn dimensions(&self) -> (u32, u32) {
+        (self.width, self.height)
+    }
+
+    pub fn scale_factor(&self) -> f32 {
+        self.scale_factor
+    }
+
+    pub(crate) fn transformation(&self) -> Transformation {
+        self.transformation
+    }
+}
diff --git a/wgpu/src/window.rs b/wgpu/src/window.rs
new file mode 100644
index 00000000..97cac9b7
--- /dev/null
+++ b/wgpu/src/window.rs
@@ -0,0 +1,5 @@
+mod backend;
+mod swap_chain;
+
+pub use backend::Backend;
+pub use swap_chain::SwapChain;
diff --git a/wgpu/src/window/backend.rs b/wgpu/src/window/backend.rs
new file mode 100644
index 00000000..fda34a0a
--- /dev/null
+++ b/wgpu/src/window/backend.rs
@@ -0,0 +1,99 @@
+use crate::{window::SwapChain, Renderer, Settings, Target};
+
+use iced_native::MouseCursor;
+use raw_window_handle::HasRawWindowHandle;
+
+#[derive(Debug)]
+pub struct Backend {
+    device: wgpu::Device,
+    queue: wgpu::Queue,
+}
+
+impl iced_native::window::Backend for Backend {
+    type Settings = Settings;
+    type Renderer = Renderer;
+    type Surface = wgpu::Surface;
+    type SwapChain = SwapChain;
+
+    fn new(settings: Self::Settings) -> (Backend, Renderer) {
+        let adapter = wgpu::Adapter::request(&wgpu::RequestAdapterOptions {
+            power_preference: wgpu::PowerPreference::Default,
+            backends: wgpu::BackendBit::all(),
+        })
+        .expect("Request adapter");
+
+        let (mut device, queue) =
+            adapter.request_device(&wgpu::DeviceDescriptor {
+                extensions: wgpu::Extensions {
+                    anisotropic_filtering: false,
+                },
+                limits: wgpu::Limits { max_bind_groups: 2 },
+            });
+
+        let renderer = Renderer::new(settings, &mut device);
+
+        (Backend { device, queue }, renderer)
+    }
+
+    fn create_surface<W: HasRawWindowHandle>(
+        &mut self,
+        window: &W,
+    ) -> wgpu::Surface {
+        wgpu::Surface::create(window)
+    }
+
+    fn create_swap_chain(
+        &mut self,
+        surface: &Self::Surface,
+        width: u32,
+        height: u32,
+        scale_factor: f64,
+    ) -> SwapChain {
+        SwapChain::new(&self.device, surface, width, height, scale_factor)
+    }
+
+    fn draw<T: AsRef<str>>(
+        &mut self,
+        renderer: &mut Self::Renderer,
+        swap_chain: &mut SwapChain,
+        output: &<Self::Renderer as iced_native::Renderer>::Output,
+        overlay: &[T],
+    ) -> MouseCursor {
+        let (frame, viewport) = swap_chain.next_frame();
+
+        let mut encoder = self.device.create_command_encoder(
+            &wgpu::CommandEncoderDescriptor { todo: 0 },
+        );
+
+        let _ = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
+            color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
+                attachment: &frame.view,
+                resolve_target: None,
+                load_op: wgpu::LoadOp::Clear,
+                store_op: wgpu::StoreOp::Store,
+                clear_color: wgpu::Color {
+                    r: 1.0,
+                    g: 1.0,
+                    b: 1.0,
+                    a: 1.0,
+                },
+            }],
+            depth_stencil_attachment: None,
+        });
+
+        let mouse_cursor = renderer.draw(
+            &mut self.device,
+            &mut encoder,
+            Target {
+                texture: &frame.view,
+                viewport,
+            },
+            output,
+            overlay,
+        );
+
+        self.queue.submit(&[encoder.finish()]);
+
+        mouse_cursor
+    }
+}
diff --git a/wgpu/src/window/swap_chain.rs b/wgpu/src/window/swap_chain.rs
new file mode 100644
index 00000000..46aaa869
--- /dev/null
+++ b/wgpu/src/window/swap_chain.rs
@@ -0,0 +1,49 @@
+use crate::Viewport;
+
+/// The rendering target of a window.
+///
+/// It represents a series of virtual framebuffers with a scale factor.
+#[derive(Debug)]
+pub struct SwapChain {
+    raw: wgpu::SwapChain,
+    viewport: Viewport,
+}
+
+impl SwapChain {}
+
+impl SwapChain {
+    pub fn new(
+        device: &wgpu::Device,
+        surface: &wgpu::Surface,
+        width: u32,
+        height: u32,
+        scale_factor: f64,
+    ) -> SwapChain {
+        SwapChain {
+            raw: new_swap_chain(surface, width, height, device),
+            viewport: Viewport::new(width, height, scale_factor),
+        }
+    }
+
+    pub fn next_frame(&mut self) -> (wgpu::SwapChainOutput<'_>, &Viewport) {
+        (self.raw.get_next_texture(), &self.viewport)
+    }
+}
+
+fn new_swap_chain(
+    surface: &wgpu::Surface,
+    width: u32,
+    height: u32,
+    device: &wgpu::Device,
+) -> wgpu::SwapChain {
+    device.create_swap_chain(
+        &surface,
+        &wgpu::SwapChainDescriptor {
+            usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
+            format: wgpu::TextureFormat::Bgra8UnormSrgb,
+            width,
+            height,
+            present_mode: wgpu::PresentMode::Vsync,
+        },
+    )
+}
-- 
cgit