Consolidate Renderer
state and support multiple render passes
This commit is contained in:
parent
ac400f6122
commit
26860496a8
88
src/lib.rs
88
src/lib.rs
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
use std::error::Error as StdError;
|
use std::error::Error as StdError;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use vk_shader_macros::include_glsl;
|
use vk_shader_macros::include_glsl;
|
||||||
pub use wgpu;
|
pub use wgpu;
|
||||||
|
@ -31,13 +32,13 @@ pub struct SurfaceTexture<'a> {
|
||||||
/// See [`PixelsBuilder`] for building a customized pixel buffer.
|
/// See [`PixelsBuilder`] for building a customized pixel buffer.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Pixels {
|
pub struct Pixels {
|
||||||
device: wgpu::Device,
|
// WGPU state
|
||||||
|
device: Rc<wgpu::Device>,
|
||||||
queue: wgpu::Queue,
|
queue: wgpu::Queue,
|
||||||
renderer: Renderer,
|
|
||||||
swap_chain: wgpu::SwapChain,
|
swap_chain: wgpu::SwapChain,
|
||||||
texture_extent: wgpu::Extent3d,
|
|
||||||
texture: wgpu::Texture,
|
// List of render passes
|
||||||
texture_format_size: u32,
|
renderers: Vec<Box<dyn RenderPass>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A builder to help create customized pixel buffers.
|
/// A builder to help create customized pixel buffers.
|
||||||
|
@ -50,13 +51,18 @@ 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 implements RenderPass.
|
/// Renderer implements RenderPass.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Renderer {
|
struct Renderer {
|
||||||
|
device: Rc<wgpu::Device>,
|
||||||
bind_group: wgpu::BindGroup,
|
bind_group: wgpu::BindGroup,
|
||||||
render_pipeline: wgpu::RenderPipeline,
|
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.
|
/// 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`].
|
/// Draw this pixel buffer to the configured [`SurfaceTexture`].
|
||||||
///
|
///
|
||||||
|
/// This executes all render passes in sequence. See [`RenderPass`].
|
||||||
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `texels` - Byte slice of texture pixels (AKA texels) to draw. The texture format can be
|
/// * `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]) {
|
pub fn render(&mut self, texels: &[u8]) {
|
||||||
// TODO: Center frame buffer in surface
|
// TODO: Center frame buffer in surface
|
||||||
let frame = self.swap_chain.get_next_texture();
|
let frame = self.swap_chain.get_next_texture();
|
||||||
|
@ -150,6 +158,31 @@ impl Pixels {
|
||||||
.device
|
.device
|
||||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor { todo: 0 });
|
.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
|
// Update the pixel buffer texture view
|
||||||
let buffer = self
|
let buffer = self
|
||||||
.device
|
.device
|
||||||
|
@ -175,17 +208,7 @@ impl Pixels {
|
||||||
self.texture_extent,
|
self.texture_extent,
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO: Run all render passes in a loop
|
// Draw the updated texture to the render target
|
||||||
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) {
|
|
||||||
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||||
color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
|
color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
|
||||||
attachment: render_target,
|
attachment: render_target,
|
||||||
|
@ -200,6 +223,10 @@ impl RenderPass for Renderer {
|
||||||
rpass.set_bind_group(0, &self.bind_group, &[]);
|
rpass.set_bind_group(0, &self.bind_group, &[]);
|
||||||
rpass.draw(0..6, 0..1);
|
rpass.draw(0..6, 0..1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn debug(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{:?}", self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> PixelsBuilder<'a> {
|
impl<'a> PixelsBuilder<'a> {
|
||||||
|
@ -234,6 +261,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(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,17 +304,29 @@ impl<'a> PixelsBuilder<'a> {
|
||||||
self
|
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.
|
/// Create a pixel buffer from the options builder.
|
||||||
///
|
///
|
||||||
/// # 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(self) -> Result<Pixels, Error> {
|
pub fn build(mut 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 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"));
|
||||||
|
@ -408,18 +448,22 @@ impl<'a> PixelsBuilder<'a> {
|
||||||
|
|
||||||
// Create a renderer that impls `RenderPass`
|
// Create a renderer that impls `RenderPass`
|
||||||
let renderer = Renderer {
|
let renderer = Renderer {
|
||||||
|
device: device.clone(),
|
||||||
bind_group,
|
bind_group,
|
||||||
render_pipeline,
|
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 {
|
Ok(Pixels {
|
||||||
device,
|
device,
|
||||||
queue,
|
queue,
|
||||||
renderer,
|
|
||||||
swap_chain,
|
swap_chain,
|
||||||
texture_extent,
|
renderers: self.renderers,
|
||||||
texture,
|
|
||||||
texture_format_size,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use std::fmt;
|
||||||
use wgpu::TextureView;
|
use wgpu::TextureView;
|
||||||
|
|
||||||
/// Objects that implement this trait can be added to [`Pixels`] as a render pass.
|
/// 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
|
/// 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`.
|
/// 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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue