Consolidate Renderer state and support multiple render passes

This commit is contained in:
Jay Oster 2019-10-04 00:58:12 -07:00
parent ac400f6122
commit 26860496a8
2 changed files with 91 additions and 23 deletions

View file

@ -11,6 +11,7 @@
use std::error::Error as StdError;
use std::fmt;
use std::rc::Rc;
use vk_shader_macros::include_glsl;
pub use wgpu;
@ -31,13 +32,13 @@ pub struct SurfaceTexture<'a> {
/// See [`PixelsBuilder`] for building a customized pixel buffer.
#[derive(Debug)]
pub struct Pixels {
device: wgpu::Device,
// WGPU state
device: Rc<wgpu::Device>,
queue: wgpu::Queue,
renderer: Renderer,
swap_chain: wgpu::SwapChain,
texture_extent: wgpu::Extent3d,
texture: wgpu::Texture,
texture_format_size: u32,
// List of render passes
renderers: Vec<Box<dyn RenderPass>>,
}
/// A builder to help create customized pixel buffers.
@ -50,13 +51,18 @@ pub struct PixelsBuilder<'a> {
pixel_aspect_ratio: f64,
surface_texture: SurfaceTexture<'a>,
texture_format: wgpu::TextureFormat,
renderers: Vec<Box<dyn RenderPass>>,
}
/// Renderer implements RenderPass.
#[derive(Debug)]
struct Renderer {
device: Rc<wgpu::Device>,
bind_group: wgpu::BindGroup,
render_pipeline: wgpu::RenderPipeline,
texture: wgpu::Texture,
texture_extent: wgpu::Extent3d,
texture_format_size: u32,
}
/// All the ways in which creating a pixel buffer can fail.
@ -139,10 +145,12 @@ impl Pixels {
/// Draw this pixel buffer to the configured [`SurfaceTexture`].
///
/// This executes all render passes in sequence. See [`RenderPass`].
///
/// # Arguments
///
/// * `texels` - Byte slice of texture pixels (AKA texels) to draw. The texture format can be
/// configured with `PixelsBuilder`.
/// configured with [`PixelsBuilder`].
pub fn render(&mut self, texels: &[u8]) {
// TODO: Center frame buffer in surface
let frame = self.swap_chain.get_next_texture();
@ -150,6 +158,31 @@ impl Pixels {
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor { todo: 0 });
// Execute all render passes
for renderer in self.renderers.iter() {
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<wgpu::Device> {
self.device.clone()
}
}
impl RenderPass for Renderer {
fn update_bindings(&mut self, _texture_view: &wgpu::TextureView) {}
fn render_pass(
&self,
encoder: &mut wgpu::CommandEncoder,
render_target: &wgpu::TextureView,
texels: &[u8],
) {
// Update the pixel buffer texture view
let buffer = self
.device
@ -175,17 +208,7 @@ impl Pixels {
self.texture_extent,
);
// TODO: Run all render passes in a loop
self.renderer.render_pass(&mut encoder, &frame.view);
self.queue.submit(&[encoder.finish()]);
}
}
impl RenderPass for Renderer {
fn update_bindings(&mut self, _texture_view: &wgpu::TextureView) {}
fn render_pass(&self, encoder: &mut wgpu::CommandEncoder, render_target: &wgpu::TextureView) {
// Draw the updated texture to the render target
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
attachment: render_target,
@ -200,6 +223,10 @@ impl RenderPass for Renderer {
rpass.set_bind_group(0, &self.bind_group, &[]);
rpass.draw(0..6, 0..1);
}
fn debug(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl<'a> PixelsBuilder<'a> {
@ -234,6 +261,7 @@ impl<'a> PixelsBuilder<'a> {
pixel_aspect_ratio: 1.0,
surface_texture,
texture_format: wgpu::TextureFormat::Rgba8UnormSrgb,
renderers: Vec::new(),
}
}
@ -276,17 +304,29 @@ impl<'a> PixelsBuilder<'a> {
self
}
/// Add a render pass.
///
/// Render passes are executed in the order they are added.
pub fn add_render_pass<R>(mut self, render_pass: R) -> PixelsBuilder<'a>
where
R: RenderPass + 'static,
{
self.renderers.push(Box::new(render_pass));
self
}
/// Create a pixel buffer from the options builder.
///
/// # Errors
///
/// Returns an error when a [`wgpu::Adapter`] cannot be found.
pub fn build(self) -> Result<Pixels, Error> {
pub fn build(mut self) -> Result<Pixels, Error> {
// 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 vs_module = device.create_shader_module(include_glsl!("shaders/shader.vert"));
let fs_module = device.create_shader_module(include_glsl!("shaders/shader.frag"));
@ -408,18 +448,22 @@ impl<'a> PixelsBuilder<'a> {
// Create a renderer that impls `RenderPass`
let renderer = Renderer {
device: device.clone(),
bind_group,
render_pipeline,
texture,
texture_extent,
texture_format_size,
};
// Add the default renderer to the head of the render passes list
self.renderers.insert(0, Box::new(renderer));
Ok(Pixels {
device,
queue,
renderer,
swap_chain,
texture_extent,
texture,
texture_format_size,
renderers: self.renderers,
})
}
}

View file

@ -1,3 +1,4 @@
use std::fmt;
use wgpu::TextureView;
/// Objects that implement this trait can be added to [`Pixels`] as a render pass.
@ -30,5 +31,28 @@ pub trait RenderPass {
/// Called when it is time to execute this render pass. Use the `encoder` to encode all
/// commands related to this render pass. The result must be stored to the `render_target`.
fn render_pass(&self, encoder: &mut wgpu::CommandEncoder, render_target: &TextureView);
///
/// # Arguments
/// * `encoder` - Command encoder for the render pass
/// * `render_target` - A reference to the output texture
/// * `texels` - The byte slice passed to `Pixels::render`
fn render_pass(
&self,
encoder: &mut wgpu::CommandEncoder,
render_target: &TextureView,
texels: &[u8],
);
/// This function implements [`Debug`](fmt::Debug) for trait objects.
///
/// You are encouraged to override the default impl to provide better debug messages.
fn debug(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "dyn RenderPass")
}
}
impl fmt::Debug for dyn RenderPass + 'static {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.debug(f)
}
}