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
This commit is contained in:
parent
26860496a8
commit
c994b35a5c
75
src/lib.rs
75
src/lib.rs
|
@ -9,6 +9,7 @@
|
||||||
//! your convenience. Use a windowing framework or context manager of your choice;
|
//! your convenience. Use a windowing framework or context manager of your choice;
|
||||||
//! [`winit`](https://crates.io/crates/winit) is a good place to start.
|
//! [`winit`](https://crates.io/crates/winit) is a good place to start.
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::error::Error as StdError;
|
use std::error::Error as StdError;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
@ -19,6 +20,12 @@ pub use wgpu;
|
||||||
mod render_pass;
|
mod render_pass;
|
||||||
pub use render_pass::RenderPass;
|
pub use render_pass::RenderPass;
|
||||||
|
|
||||||
|
// Type aliases for RenderPass
|
||||||
|
type RPObject = Box<dyn RenderPass>;
|
||||||
|
type RPDevice = Rc<wgpu::Device>;
|
||||||
|
type RPQueue = Rc<RefCell<wgpu::Queue>>;
|
||||||
|
type RPTexture = wgpu::TextureView;
|
||||||
|
|
||||||
/// A logical texture for a window surface.
|
/// A logical texture for a window surface.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SurfaceTexture<'a> {
|
pub struct SurfaceTexture<'a> {
|
||||||
|
@ -34,15 +41,14 @@ pub struct SurfaceTexture<'a> {
|
||||||
pub struct Pixels {
|
pub struct Pixels {
|
||||||
// WGPU state
|
// WGPU state
|
||||||
device: Rc<wgpu::Device>,
|
device: Rc<wgpu::Device>,
|
||||||
queue: wgpu::Queue,
|
queue: Rc<RefCell<wgpu::Queue>>,
|
||||||
swap_chain: wgpu::SwapChain,
|
swap_chain: wgpu::SwapChain,
|
||||||
|
|
||||||
// List of render passes
|
// List of render passes
|
||||||
renderers: Vec<Box<dyn RenderPass>>,
|
renderers: Vec<RPObject>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A builder to help create customized pixel buffers.
|
/// A builder to help create customized pixel buffers.
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct PixelsBuilder<'a> {
|
pub struct PixelsBuilder<'a> {
|
||||||
request_adapter_options: wgpu::RequestAdapterOptions,
|
request_adapter_options: wgpu::RequestAdapterOptions,
|
||||||
device_descriptor: wgpu::DeviceDescriptor,
|
device_descriptor: wgpu::DeviceDescriptor,
|
||||||
|
@ -51,7 +57,7 @@ pub struct PixelsBuilder<'a> {
|
||||||
pixel_aspect_ratio: f64,
|
pixel_aspect_ratio: f64,
|
||||||
surface_texture: SurfaceTexture<'a>,
|
surface_texture: SurfaceTexture<'a>,
|
||||||
texture_format: wgpu::TextureFormat,
|
texture_format: wgpu::TextureFormat,
|
||||||
renderers: Vec<Box<dyn RenderPass>>,
|
renderer_factories: Vec<Box<dyn Fn(RPDevice, RPQueue, &RPTexture) -> RPObject>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Renderer implements RenderPass.
|
/// Renderer implements RenderPass.
|
||||||
|
@ -160,17 +166,11 @@ impl Pixels {
|
||||||
|
|
||||||
// Execute all render passes
|
// Execute all render passes
|
||||||
for renderer in self.renderers.iter() {
|
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);
|
renderer.render_pass(&mut encoder, &frame.view, texels);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.queue.submit(&[encoder.finish()]);
|
self.queue.borrow_mut().submit(&[encoder.finish()]);
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a reference to the [`wgpu::Device`].
|
|
||||||
///
|
|
||||||
/// This is used for creating a custom [`RenderPass`].
|
|
||||||
pub fn device(&self) -> Rc<wgpu::Device> {
|
|
||||||
self.device.clone()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,10 +238,24 @@ impl<'a> PixelsBuilder<'a> {
|
||||||
/// # use pixels::PixelsBuilder;
|
/// # use pixels::PixelsBuilder;
|
||||||
/// # let surface = wgpu::Surface::create(&pixels_mocks::RWH);
|
/// # let surface = wgpu::Surface::create(&pixels_mocks::RWH);
|
||||||
/// # let surface_texture = pixels::SurfaceTexture::new(1024, 768, &surface);
|
/// # 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)
|
/// let fb = PixelsBuilder::new(256, 240, surface_texture)
|
||||||
/// .pixel_aspect_ratio(8.0 / 7.0)
|
/// .pixel_aspect_ratio(8.0 / 7.0)
|
||||||
/// # // TODO: demonstrate adding a render pass here
|
/// .add_render_pass(|device, queue, texture| {
|
||||||
/// # //.render_pass(...)
|
/// // Create reources for MyRenderPass here
|
||||||
|
/// Box::new(MyRenderPass {
|
||||||
|
/// // ...
|
||||||
|
/// })
|
||||||
|
/// })
|
||||||
/// .build()?;
|
/// .build()?;
|
||||||
/// # Ok::<(), pixels::Error>(())
|
/// # Ok::<(), pixels::Error>(())
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -261,7 +275,7 @@ impl<'a> PixelsBuilder<'a> {
|
||||||
pixel_aspect_ratio: 1.0,
|
pixel_aspect_ratio: 1.0,
|
||||||
surface_texture,
|
surface_texture,
|
||||||
texture_format: wgpu::TextureFormat::Rgba8UnormSrgb,
|
texture_format: wgpu::TextureFormat::Rgba8UnormSrgb,
|
||||||
renderers: Vec::new(),
|
renderer_factories: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -307,11 +321,18 @@ impl<'a> PixelsBuilder<'a> {
|
||||||
/// Add a render pass.
|
/// Add a render pass.
|
||||||
///
|
///
|
||||||
/// Render passes are executed in the order they are added.
|
/// Render passes are executed in the order they are added.
|
||||||
pub fn add_render_pass<R>(mut self, render_pass: R) -> PixelsBuilder<'a>
|
///
|
||||||
where
|
/// # Factory Arguments
|
||||||
R: RenderPass + 'static,
|
///
|
||||||
{
|
/// * `device` - A reference-counted [`wgpu::Device`] which allows you to create GPU resources.
|
||||||
self.renderers.push(Box::new(render_pass));
|
/// * `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
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -320,13 +341,14 @@ impl<'a> PixelsBuilder<'a> {
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// Returns an error when a [`wgpu::Adapter`] cannot be found.
|
/// Returns an error when a [`wgpu::Adapter`] cannot be found.
|
||||||
pub fn build(mut self) -> Result<Pixels, Error> {
|
pub fn build(self) -> Result<Pixels, Error> {
|
||||||
// TODO: Use `options.pixel_aspect_ratio` to stretch the scaled texture
|
// TODO: Use `options.pixel_aspect_ratio` to stretch the scaled texture
|
||||||
|
|
||||||
let adapter =
|
let adapter =
|
||||||
wgpu::Adapter::request(&self.request_adapter_options).ok_or(Error::AdapterNotFound)?;
|
wgpu::Adapter::request(&self.request_adapter_options).ok_or(Error::AdapterNotFound)?;
|
||||||
let (device, queue) = adapter.request_device(&self.device_descriptor);
|
let (device, queue) = adapter.request_device(&self.device_descriptor);
|
||||||
let device = Rc::new(device);
|
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 vs_module = device.create_shader_module(include_glsl!("shaders/shader.vert"));
|
||||||
let fs_module = device.create_shader_module(include_glsl!("shaders/shader.frag"));
|
let fs_module = device.create_shader_module(include_glsl!("shaders/shader.frag"));
|
||||||
|
@ -456,14 +478,19 @@ impl<'a> PixelsBuilder<'a> {
|
||||||
texture_format_size,
|
texture_format_size,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add the default renderer to the head of the render passes list
|
let mut renderers: Vec<Box<dyn RenderPass>> = vec![Box::new(renderer)];
|
||||||
self.renderers.insert(0, 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 {
|
Ok(Pixels {
|
||||||
device,
|
device,
|
||||||
queue,
|
queue,
|
||||||
swap_chain,
|
swap_chain,
|
||||||
renderers: self.renderers,
|
renderers,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue