From c994b35a5cf0877222859e2c594701e2f85e358d Mon Sep 17 00:00:00 2001 From: Jay Oster Date: Fri, 4 Oct 2019 20:52:32 -0700 Subject: [PATCH] Settle on an interface for `PixelsBuilder::add_render_pass()` - Accepts a closure (factory function) which itself accepts references to the `wgpu` instances needed for rendering - This solves a need for creating `RenderPass` objects with a lot of `Option` types and trying to update them dynamically after the fact - In other words, this is an RAII implementation - `RenderPass` objects are created during the `PixelsBuilder` build stage - It should prevent patterns where half-created `RenderPass` objects are necessary --- src/lib.rs | 75 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 51 insertions(+), 24 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ee3130a..c9443eb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,7 @@ //! your convenience. Use a windowing framework or context manager of your choice; //! [`winit`](https://crates.io/crates/winit) is a good place to start. +use std::cell::RefCell; use std::error::Error as StdError; use std::fmt; use std::rc::Rc; @@ -19,6 +20,12 @@ pub use wgpu; mod render_pass; pub use render_pass::RenderPass; +// Type aliases for RenderPass +type RPObject = Box; +type RPDevice = Rc; +type RPQueue = Rc>; +type RPTexture = wgpu::TextureView; + /// A logical texture for a window surface. #[derive(Debug)] pub struct SurfaceTexture<'a> { @@ -34,15 +41,14 @@ pub struct SurfaceTexture<'a> { pub struct Pixels { // WGPU state device: Rc, - queue: wgpu::Queue, + queue: Rc>, swap_chain: wgpu::SwapChain, // List of render passes - renderers: Vec>, + renderers: Vec, } /// A builder to help create customized pixel buffers. -#[derive(Debug)] pub struct PixelsBuilder<'a> { request_adapter_options: wgpu::RequestAdapterOptions, device_descriptor: wgpu::DeviceDescriptor, @@ -51,7 +57,7 @@ pub struct PixelsBuilder<'a> { pixel_aspect_ratio: f64, surface_texture: SurfaceTexture<'a>, texture_format: wgpu::TextureFormat, - renderers: Vec>, + renderer_factories: Vec RPObject>>, } /// Renderer implements RenderPass. @@ -160,17 +166,11 @@ impl Pixels { // Execute all render passes for renderer in self.renderers.iter() { + // TODO: Create a texture chain so that each pass receives the texture drawn by the previous renderer.render_pass(&mut encoder, &frame.view, texels); } - self.queue.submit(&[encoder.finish()]); - } - - /// Get a reference to the [`wgpu::Device`]. - /// - /// This is used for creating a custom [`RenderPass`]. - pub fn device(&self) -> Rc { - self.device.clone() + self.queue.borrow_mut().submit(&[encoder.finish()]); } } @@ -238,10 +238,24 @@ impl<'a> PixelsBuilder<'a> { /// # use pixels::PixelsBuilder; /// # let surface = wgpu::Surface::create(&pixels_mocks::RWH); /// # let surface_texture = pixels::SurfaceTexture::new(1024, 768, &surface); + /// struct MyRenderPass { + /// // ... + /// }; + /// + /// impl pixels::RenderPass for MyRenderPass { + /// // ... + /// # fn update_bindings(&mut self, _: &wgpu::TextureView) {} + /// # fn render_pass(&self, _: &mut wgpu::CommandEncoder, _: &wgpu::TextureView, _: &[u8]) {} + /// } + /// /// let fb = PixelsBuilder::new(256, 240, surface_texture) /// .pixel_aspect_ratio(8.0 / 7.0) - /// # // TODO: demonstrate adding a render pass here - /// # //.render_pass(...) + /// .add_render_pass(|device, queue, texture| { + /// // Create reources for MyRenderPass here + /// Box::new(MyRenderPass { + /// // ... + /// }) + /// }) /// .build()?; /// # Ok::<(), pixels::Error>(()) /// ``` @@ -261,7 +275,7 @@ impl<'a> PixelsBuilder<'a> { pixel_aspect_ratio: 1.0, surface_texture, texture_format: wgpu::TextureFormat::Rgba8UnormSrgb, - renderers: Vec::new(), + renderer_factories: Vec::new(), } } @@ -307,11 +321,18 @@ impl<'a> PixelsBuilder<'a> { /// Add a render pass. /// /// Render passes are executed in the order they are added. - pub fn add_render_pass(mut self, render_pass: R) -> PixelsBuilder<'a> - where - R: RenderPass + 'static, - { - self.renderers.push(Box::new(render_pass)); + /// + /// # Factory Arguments + /// + /// * `device` - A reference-counted [`wgpu::Device`] which allows you to create GPU resources. + /// * `queue` - A reference-counted [`wgpu::Queue`] which can execute command buffers. + /// * `texture` - A [`wgpu::TextureView`] reference that is used as the texture input for the + /// render pass. + pub fn add_render_pass( + mut self, + factory: impl Fn(RPDevice, RPQueue, &RPTexture) -> RPObject + 'static, + ) -> PixelsBuilder<'a> { + self.renderer_factories.push(Box::new(factory)); self } @@ -320,13 +341,14 @@ impl<'a> PixelsBuilder<'a> { /// # Errors /// /// Returns an error when a [`wgpu::Adapter`] cannot be found. - pub fn build(mut self) -> Result { + pub fn build(self) -> Result { // TODO: Use `options.pixel_aspect_ratio` to stretch the scaled texture let adapter = wgpu::Adapter::request(&self.request_adapter_options).ok_or(Error::AdapterNotFound)?; let (device, queue) = adapter.request_device(&self.device_descriptor); let device = Rc::new(device); + let queue = Rc::new(RefCell::new(queue)); let vs_module = device.create_shader_module(include_glsl!("shaders/shader.vert")); let fs_module = device.create_shader_module(include_glsl!("shaders/shader.frag")); @@ -456,14 +478,19 @@ impl<'a> PixelsBuilder<'a> { texture_format_size, }; - // Add the default renderer to the head of the render passes list - self.renderers.insert(0, Box::new(renderer)); + let mut renderers: Vec> = vec![Box::new(renderer)]; + + // Create all render passes + renderers.extend(self.renderer_factories.iter().map(|f| { + // TODO: Create a texture chain so that each pass recieves the texture drawn by the previous + f(device.clone(), queue.clone(), &texture_view) + })); Ok(Pixels { device, queue, swap_chain, - renderers: self.renderers, + renderers, }) } }