New render api (#96)
* Extreme WIP: New wgpu access api * Add getters for device and queue to Pixels * Don't run ScalingRenderer in render_custom() * Expose Pixels::scaling_renderer * Fix exposing ScalingRenderer * Tweak Pixels::render_custom() * Cleanup * More cleanup * Fix doc comment * Clippy * Fix doctests Co-authored-by: JMS55 <47158642+JMS55@users.noreply.github.com>
This commit is contained in:
parent
755b1fed28
commit
27e7bfe998
245
src/lib.rs
245
src/lib.rs
|
@ -28,23 +28,16 @@
|
||||||
#![deny(clippy::all)]
|
#![deny(clippy::all)]
|
||||||
#![forbid(unsafe_code)]
|
#![forbid(unsafe_code)]
|
||||||
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
pub use crate::macros::*;
|
pub use crate::macros::*;
|
||||||
pub use crate::render_pass::{BoxedRenderPass, Device, Queue, RenderPass};
|
pub use crate::renderers::ScalingRenderer;
|
||||||
use crate::renderers::Renderer;
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
pub use wgpu;
|
pub use wgpu;
|
||||||
use wgpu::{Extent3d, TextureView};
|
|
||||||
|
|
||||||
mod macros;
|
mod macros;
|
||||||
mod render_pass;
|
|
||||||
mod renderers;
|
mod renderers;
|
||||||
|
|
||||||
type RenderPassFactory = Box<dyn Fn(Device, Queue, &TextureView, &Extent3d) -> BoxedRenderPass>;
|
|
||||||
|
|
||||||
/// A logical texture for a window surface.
|
/// A logical texture for a window surface.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SurfaceTexture {
|
pub struct SurfaceTexture {
|
||||||
|
@ -59,14 +52,14 @@ pub struct SurfaceTexture {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Pixels {
|
pub struct Pixels {
|
||||||
// WGPU state
|
// WGPU state
|
||||||
device: Rc<wgpu::Device>,
|
device: wgpu::Device,
|
||||||
queue: Rc<RefCell<wgpu::Queue>>,
|
queue: wgpu::Queue,
|
||||||
swap_chain: wgpu::SwapChain,
|
swap_chain: wgpu::SwapChain,
|
||||||
surface_texture: SurfaceTexture,
|
surface_texture: SurfaceTexture,
|
||||||
present_mode: wgpu::PresentMode,
|
present_mode: wgpu::PresentMode,
|
||||||
|
|
||||||
// List of render passes
|
// A default renderer to scale the input texture to the screen size
|
||||||
renderers: Vec<BoxedRenderPass>,
|
scaling_renderer: ScalingRenderer,
|
||||||
|
|
||||||
// Texture state for the texel upload
|
// Texture state for the texel upload
|
||||||
texture: wgpu::Texture,
|
texture: wgpu::Texture,
|
||||||
|
@ -90,7 +83,6 @@ pub struct PixelsBuilder<'req> {
|
||||||
present_mode: wgpu::PresentMode,
|
present_mode: wgpu::PresentMode,
|
||||||
surface_texture: SurfaceTexture,
|
surface_texture: SurfaceTexture,
|
||||||
texture_format: wgpu::TextureFormat,
|
texture_format: wgpu::TextureFormat,
|
||||||
renderer_factories: Vec<RenderPassFactory>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// All the ways in which creating a pixel buffer can fail.
|
/// All the ways in which creating a pixel buffer can fail.
|
||||||
|
@ -176,8 +168,6 @@ impl Pixels {
|
||||||
/// Call this method in response to a resize event from your window manager. The size expected
|
/// Call this method in response to a resize event from your window manager. The size expected
|
||||||
/// is in physical pixel units.
|
/// is in physical pixel units.
|
||||||
pub fn resize(&mut self, width: u32, height: u32) {
|
pub fn resize(&mut self, width: u32, height: u32) {
|
||||||
// TODO: Call `update_bindings` on each render pass to create a texture chain
|
|
||||||
|
|
||||||
// Update SurfaceTexture dimensions
|
// Update SurfaceTexture dimensions
|
||||||
self.surface_texture.width = width;
|
self.surface_texture.width = width;
|
||||||
self.surface_texture.height = height;
|
self.surface_texture.height = height;
|
||||||
|
@ -209,21 +199,83 @@ impl Pixels {
|
||||||
let mut encoder = self
|
let mut encoder = self
|
||||||
.device
|
.device
|
||||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
|
.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
|
||||||
for renderer in self.renderers.iter_mut() {
|
self.scaling_renderer
|
||||||
renderer.resize(&mut encoder, width, height);
|
.resize(&mut self.device, &mut encoder, width, height);
|
||||||
}
|
self.queue.submit(&[encoder.finish()]);
|
||||||
|
|
||||||
self.queue.borrow_mut().submit(&[encoder.finish()]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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`].
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns an error when [`wgpu::SwapChain::get_next_texture`] times out.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// # use pixels::Pixels;
|
||||||
|
/// # let surface = wgpu::Surface::create(&pixels_mocks::RWH);
|
||||||
|
/// # let surface_texture = pixels::SurfaceTexture::new(1024, 768, surface);
|
||||||
|
/// let mut pixels = Pixels::new(320, 240, surface_texture)?;
|
||||||
|
///
|
||||||
|
/// // Clear the pixel buffer
|
||||||
|
/// let frame = pixels.get_frame();
|
||||||
|
/// for pixel in frame.chunks_exact_mut(4) {
|
||||||
|
/// pixel[0] = 0x00; // R
|
||||||
|
/// pixel[1] = 0x00; // G
|
||||||
|
/// pixel[2] = 0x00; // B
|
||||||
|
/// pixel[3] = 0xff; // A
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// // Draw it to the `SurfaceTexture`
|
||||||
|
/// pixels.render();
|
||||||
|
/// # Ok::<(), pixels::Error>(())
|
||||||
|
/// ```
|
||||||
|
pub fn render(&mut self) -> Result<(), Error> {
|
||||||
|
self.render_with(|encoder, render_target, scaling_renderer| {
|
||||||
|
scaling_renderer.render(encoder, render_target);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draw this pixel buffer to the configured [`SurfaceTexture`],
|
||||||
|
/// using a custom user-provided render function.
|
||||||
|
///
|
||||||
|
/// Provides access to a [`wgpu::CommandEncoder`],
|
||||||
|
/// a [`wgpu::TextureView`] from the swapchain which you can use to render to the screen,
|
||||||
|
/// and the default [`ScalingRenderer`].
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// Returns an error when [`wgpu::SwapChain::get_next_texture`] times out.
|
/// Returns an error when [`wgpu::SwapChain::get_next_texture`] times out.
|
||||||
pub fn render(&mut self) -> Result<(), Error> {
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// # use pixels::Pixels;
|
||||||
|
/// # let surface = wgpu::Surface::create(&pixels_mocks::RWH);
|
||||||
|
/// # let surface_texture = pixels::SurfaceTexture::new(1024, 768, surface);
|
||||||
|
/// let mut pixels = Pixels::new(320, 240, surface_texture)?;
|
||||||
|
///
|
||||||
|
/// // Clear the pixel buffer
|
||||||
|
/// let frame = pixels.get_frame();
|
||||||
|
/// for pixel in frame.chunks_exact_mut(4) {
|
||||||
|
/// pixel[0] = 0x00; // R
|
||||||
|
/// pixel[1] = 0x00; // G
|
||||||
|
/// pixel[2] = 0x00; // B
|
||||||
|
/// pixel[3] = 0xff; // A
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// // Draw it to the `SurfaceTexture`
|
||||||
|
/// pixels.render_with(|encoder, render_target, scaling_renderer| {
|
||||||
|
/// scaling_renderer.render(encoder, render_target);
|
||||||
|
/// // etc...
|
||||||
|
/// });
|
||||||
|
/// # Ok::<(), pixels::Error>(())
|
||||||
|
/// ```
|
||||||
|
pub fn render_with<F>(&mut self, render_function: F) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut wgpu::CommandEncoder, &wgpu::TextureView, &ScalingRenderer),
|
||||||
|
{
|
||||||
// TODO: Center frame buffer in surface
|
// TODO: Center frame buffer in surface
|
||||||
let frame = self
|
let frame = self
|
||||||
.swap_chain
|
.swap_chain
|
||||||
|
@ -258,40 +310,15 @@ impl Pixels {
|
||||||
self.texture_extent,
|
self.texture_extent,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Execute all render passes
|
// Call the users render function.
|
||||||
for renderer in self.renderers.iter() {
|
(render_function)(&mut encoder, &frame.view, &self.scaling_renderer);
|
||||||
// TODO: Create a texture chain so that each pass receives the texture drawn by the previous
|
|
||||||
renderer.render(&mut encoder, &frame.view);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.queue.borrow_mut().submit(&[encoder.finish()]);
|
self.queue.submit(&[encoder.finish()]);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a mutable byte slice for the pixel buffer. The buffer is _not_ cleared for you; it will
|
/// Get a mutable byte slice for the pixel buffer. The buffer is _not_ cleared for you; it will
|
||||||
/// retain the previous frame's contents until you clear it yourself.
|
/// retain the previous frame's contents until you clear it yourself.
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```no_run
|
|
||||||
/// # use pixels::Pixels;
|
|
||||||
/// # let surface = wgpu::Surface::create(&pixels_mocks::RWH);
|
|
||||||
/// # let surface_texture = pixels::SurfaceTexture::new(1024, 768, surface);
|
|
||||||
/// let mut pixels = Pixels::new(320, 240, surface_texture)?;
|
|
||||||
///
|
|
||||||
/// // Clear the pixel buffer
|
|
||||||
/// let frame = pixels.get_frame();
|
|
||||||
/// for pixel in frame.chunks_exact_mut(4) {
|
|
||||||
/// pixel[0] = 0x00; // R
|
|
||||||
/// pixel[1] = 0x00; // G
|
|
||||||
/// pixel[2] = 0x00; // B
|
|
||||||
/// pixel[3] = 0xff; // A
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// // Draw it to the `SurfaceTexture`
|
|
||||||
/// pixels.render();
|
|
||||||
/// # Ok::<(), pixels::Error>(())
|
|
||||||
/// ```
|
|
||||||
pub fn get_frame(&mut self) -> &mut [u8] {
|
pub fn get_frame(&mut self) -> &mut [u8] {
|
||||||
&mut self.pixels
|
&mut self.pixels
|
||||||
}
|
}
|
||||||
|
@ -388,6 +415,21 @@ impl Pixels {
|
||||||
pos.1.max(0).min(self.texture_extent.height as isize - 1) as usize,
|
pos.1.max(0).min(self.texture_extent.height as isize - 1) as usize,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Provides access the the [`wgpu::Device`] `pixels` uses.
|
||||||
|
pub fn device(&self) -> &wgpu::Device {
|
||||||
|
&self.device
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provides access the the [`wgpu::Queue`] `pixels` uses.
|
||||||
|
pub fn queue(&self) -> &wgpu::Queue {
|
||||||
|
&self.queue
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provides access the the [`wgpu::Texture`] `pixels` makes by uploading the frame you provide to the GPU.
|
||||||
|
pub fn texture(&self) -> &wgpu::Texture {
|
||||||
|
&self.texture
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'req> PixelsBuilder<'req> {
|
impl<'req> PixelsBuilder<'req> {
|
||||||
|
@ -399,24 +441,12 @@ impl<'req> PixelsBuilder<'req> {
|
||||||
/// # 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, _: &wgpu::Extent3d) {}
|
|
||||||
/// # fn render(&self, _: &mut wgpu::CommandEncoder, _: &wgpu::TextureView) {}
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// let mut pixels = PixelsBuilder::new(256, 240, surface_texture)
|
/// let mut pixels = PixelsBuilder::new(256, 240, surface_texture)
|
||||||
|
/// .request_adapter_options(wgpu::RequestAdapterOptions {
|
||||||
|
/// power_preference: wgpu::PowerPreference::HighPerformance,
|
||||||
|
/// compatible_surface: None,
|
||||||
|
/// })
|
||||||
/// .pixel_aspect_ratio(8.0 / 7.0)
|
/// .pixel_aspect_ratio(8.0 / 7.0)
|
||||||
/// .add_render_pass(|device, queue, texture, texture_size| {
|
|
||||||
/// // Create reources for MyRenderPass here
|
|
||||||
/// Box::new(MyRenderPass {
|
|
||||||
/// // ...
|
|
||||||
/// })
|
|
||||||
/// })
|
|
||||||
/// .build()?;
|
/// .build()?;
|
||||||
/// # Ok::<(), pixels::Error>(())
|
/// # Ok::<(), pixels::Error>(())
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -438,7 +468,6 @@ impl<'req> PixelsBuilder<'req> {
|
||||||
present_mode: wgpu::PresentMode::Fifo,
|
present_mode: wgpu::PresentMode::Fifo,
|
||||||
surface_texture,
|
surface_texture,
|
||||||
texture_format: wgpu::TextureFormat::Rgba8UnormSrgb,
|
texture_format: wgpu::TextureFormat::Rgba8UnormSrgb,
|
||||||
renderer_factories: Vec::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -523,62 +552,6 @@ impl<'req> PixelsBuilder<'req> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a render pass.
|
|
||||||
///
|
|
||||||
/// Render passes are executed in the order they are added.
|
|
||||||
///
|
|
||||||
/// # 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.
|
|
||||||
/// * `texture_size` - A [`wgpu::Extent3d`] providing the input texture size.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```no_run
|
|
||||||
/// use pixels::{BoxedRenderPass, Device, PixelsBuilder, Queue, RenderPass};
|
|
||||||
/// use pixels::wgpu::{Extent3d, TextureView};
|
|
||||||
///
|
|
||||||
/// struct MyRenderPass {
|
|
||||||
/// device: Device,
|
|
||||||
/// queue: Queue,
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// impl MyRenderPass {
|
|
||||||
/// fn factory(
|
|
||||||
/// device: Device,
|
|
||||||
/// queue: Queue,
|
|
||||||
/// texture: &TextureView,
|
|
||||||
/// texture_size: &Extent3d,
|
|
||||||
/// ) -> BoxedRenderPass {
|
|
||||||
/// // Create a bind group, pipeline, etc. and store all of the necessary state...
|
|
||||||
/// Box::new(MyRenderPass { device, queue })
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// impl RenderPass for MyRenderPass {
|
|
||||||
/// // ...
|
|
||||||
/// # fn update_bindings(&mut self, _: &wgpu::TextureView, _: &wgpu::Extent3d) {}
|
|
||||||
/// # fn render(&self, _: &mut wgpu::CommandEncoder, _: &wgpu::TextureView) {}
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// # let surface = wgpu::Surface::create(&pixels_mocks::RWH);
|
|
||||||
/// # let surface_texture = pixels::SurfaceTexture::new(1024, 768, surface);
|
|
||||||
/// let builder = PixelsBuilder::new(320, 240, surface_texture)
|
|
||||||
/// .add_render_pass(MyRenderPass::factory)
|
|
||||||
/// .build()?;
|
|
||||||
/// # Ok::<(), pixels::Error>(())
|
|
||||||
/// ```
|
|
||||||
pub fn add_render_pass(
|
|
||||||
mut self,
|
|
||||||
factory: impl Fn(Device, Queue, &TextureView, &Extent3d) -> BoxedRenderPass + 'static,
|
|
||||||
) -> PixelsBuilder<'req> {
|
|
||||||
self.renderer_factories.push(Box::new(factory));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a pixel buffer from the options builder.
|
/// Create a pixel buffer from the options builder.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
|
@ -602,9 +575,8 @@ impl<'req> PixelsBuilder<'req> {
|
||||||
))
|
))
|
||||||
.ok_or(Error::AdapterNotFound)?;
|
.ok_or(Error::AdapterNotFound)?;
|
||||||
|
|
||||||
let (device, queue) = pollster::block_on(adapter.request_device(&self.device_descriptor));
|
let (mut device, queue) =
|
||||||
let device = Rc::new(device);
|
pollster::block_on(adapter.request_device(&self.device_descriptor));
|
||||||
let queue = Rc::new(RefCell::new(queue));
|
|
||||||
|
|
||||||
// The rest of this is technically a fixed-function pipeline... For now!
|
// The rest of this is technically a fixed-function pipeline... For now!
|
||||||
|
|
||||||
|
@ -656,24 +628,7 @@ impl<'req> PixelsBuilder<'req> {
|
||||||
.transform
|
.transform
|
||||||
.inversed();
|
.inversed();
|
||||||
|
|
||||||
// Create a renderer that impls `RenderPass`
|
let scaling_renderer = ScalingRenderer::new(&mut device, &texture_view, &texture_extent);
|
||||||
let mut renderers = vec![Renderer::factory(
|
|
||||||
device.clone(),
|
|
||||||
queue.clone(),
|
|
||||||
&texture_view,
|
|
||||||
&texture_extent,
|
|
||||||
)];
|
|
||||||
|
|
||||||
// Create all render passes
|
|
||||||
renderers.extend(self.renderer_factories.iter().map(|f| {
|
|
||||||
// TODO: Create a texture chain so that each pass receives the texture drawn by the previous
|
|
||||||
f(
|
|
||||||
device.clone(),
|
|
||||||
queue.clone(),
|
|
||||||
&texture_view,
|
|
||||||
&texture_extent,
|
|
||||||
)
|
|
||||||
}));
|
|
||||||
|
|
||||||
Ok(Pixels {
|
Ok(Pixels {
|
||||||
device,
|
device,
|
||||||
|
@ -681,7 +636,7 @@ impl<'req> PixelsBuilder<'req> {
|
||||||
swap_chain,
|
swap_chain,
|
||||||
surface_texture,
|
surface_texture,
|
||||||
present_mode,
|
present_mode,
|
||||||
renderers,
|
scaling_renderer,
|
||||||
texture,
|
texture,
|
||||||
texture_extent,
|
texture_extent,
|
||||||
texture_format_size,
|
texture_format_size,
|
||||||
|
|
|
@ -1,80 +0,0 @@
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::fmt;
|
|
||||||
use std::rc::Rc;
|
|
||||||
use wgpu::{Extent3d, TextureView};
|
|
||||||
|
|
||||||
/// A reference-counted [`wgpu::Device`]
|
|
||||||
pub type Device = Rc<wgpu::Device>;
|
|
||||||
|
|
||||||
/// A reference-counted [`wgpu::Queue`] (with interior mutability)
|
|
||||||
pub type Queue = Rc<RefCell<wgpu::Queue>>;
|
|
||||||
|
|
||||||
/// The boxed render pass type for dynamic dispatch
|
|
||||||
pub type BoxedRenderPass = Box<dyn RenderPass>;
|
|
||||||
|
|
||||||
/// Objects that implement this trait can be added to [`Pixels`] as a render pass.
|
|
||||||
///
|
|
||||||
/// [`Pixels`] always has at least one render pass; a scaling pass that uses a nearest-neighbor
|
|
||||||
/// sampler to preserve pixel edges. Optionally it may also have a second scaling pass that
|
|
||||||
/// transforms the texture to its final size (for non-square pixel aspect ratios). During this
|
|
||||||
/// second pass, the texture is stretched horizontally using a linear sampler.
|
|
||||||
///
|
|
||||||
/// Any additional render passes are executed afterward.
|
|
||||||
///
|
|
||||||
/// Each render pass is configured with one [`wgpu::TextureView`] as an input. You will probably
|
|
||||||
/// want to create a binding for this `texture_view` so your shaders can sample from it.
|
|
||||||
///
|
|
||||||
/// The render pass will also receive a reference to another [`wgpu::TextureView`] when the pass is
|
|
||||||
/// executed. This texture view is the `render_target`.
|
|
||||||
///
|
|
||||||
/// [`Pixels`]: ./struct.Pixels.html
|
|
||||||
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`.
|
|
||||||
///
|
|
||||||
/// # 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(&self, encoder: &mut wgpu::CommandEncoder, render_target: &TextureView);
|
|
||||||
|
|
||||||
/// This method will be called when the input [`wgpu::TextureView`] needs to be rebinded.
|
|
||||||
///
|
|
||||||
/// A [`wgpu::TextureView`] is provided to the `RenderPass` factory as an input texture with
|
|
||||||
/// the original [`SurfaceTexture`] size. This method is called in response to resizing the
|
|
||||||
/// [`SurfaceTexture`], where your `RenderPass` impl can update its input texture for the new
|
|
||||||
/// size.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
/// * `input_texture` - A reference to the `TextureView` for this render pass's input
|
|
||||||
/// * `input_texture_size` - The `input_texture` size
|
|
||||||
///
|
|
||||||
/// [`Pixels`]: ./struct.Pixels.html
|
|
||||||
/// [`SurfaceTexture`]: ./struct.SurfaceTexture.html
|
|
||||||
fn update_bindings(&mut self, input_texture: &TextureView, input_texture_size: &Extent3d);
|
|
||||||
|
|
||||||
/// When the window is resized, this method will be called, allowing the render pass to
|
|
||||||
/// customize itself to the display size.
|
|
||||||
///
|
|
||||||
/// The default implementation is a no-op.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
/// * `encoder` - Command encoder for the render pass
|
|
||||||
/// * `width` - Render target width in physical pixel units
|
|
||||||
/// * `height` - Render target height in physical pixel units
|
|
||||||
#[allow(unused_variables)]
|
|
||||||
fn resize(&mut self, encoder: &mut wgpu::CommandEncoder, width: u32, height: u32) {}
|
|
||||||
|
|
||||||
/// 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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +1,9 @@
|
||||||
use std::fmt;
|
|
||||||
use std::rc::Rc;
|
|
||||||
use ultraviolet::Mat4;
|
|
||||||
use wgpu::{self, Extent3d, TextureView};
|
|
||||||
|
|
||||||
use crate::include_spv;
|
use crate::include_spv;
|
||||||
use crate::render_pass::{BoxedRenderPass, Device, Queue, RenderPass};
|
use ultraviolet::Mat4;
|
||||||
|
|
||||||
/// Renderer implements [`RenderPass`].
|
/// The default renderer that scales your frame to the screen size.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct Renderer {
|
pub struct ScalingRenderer {
|
||||||
device: Rc<wgpu::Device>,
|
|
||||||
uniform_buffer: wgpu::Buffer,
|
uniform_buffer: wgpu::Buffer,
|
||||||
bind_group: wgpu::BindGroup,
|
bind_group: wgpu::BindGroup,
|
||||||
render_pipeline: wgpu::RenderPipeline,
|
render_pipeline: wgpu::RenderPipeline,
|
||||||
|
@ -17,14 +11,12 @@ pub(crate) struct Renderer {
|
||||||
height: f32,
|
height: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Renderer {
|
impl ScalingRenderer {
|
||||||
/// Factory function for generating `RenderPass` trait objects.
|
pub(crate) fn new(
|
||||||
pub(crate) fn factory(
|
device: &mut wgpu::Device,
|
||||||
device: Device,
|
texture_view: &wgpu::TextureView,
|
||||||
_queue: Queue,
|
texture_size: &wgpu::Extent3d,
|
||||||
texture_view: &TextureView,
|
) -> Self {
|
||||||
texture_size: &Extent3d,
|
|
||||||
) -> BoxedRenderPass {
|
|
||||||
let vs_module = device.create_shader_module(include_spv!("../shaders/vert.spv"));
|
let vs_module = device.create_shader_module(include_spv!("../shaders/vert.spv"));
|
||||||
let fs_module = device.create_shader_module(include_spv!("../shaders/frag.spv"));
|
let fs_module = device.create_shader_module(include_spv!("../shaders/frag.spv"));
|
||||||
|
|
||||||
|
@ -139,19 +131,16 @@ impl Renderer {
|
||||||
alpha_to_coverage_enabled: false,
|
alpha_to_coverage_enabled: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
Box::new(Renderer {
|
Self {
|
||||||
device,
|
|
||||||
uniform_buffer,
|
uniform_buffer,
|
||||||
bind_group,
|
bind_group,
|
||||||
render_pipeline,
|
render_pipeline,
|
||||||
width: texture_size.width as f32,
|
width: texture_size.width as f32,
|
||||||
height: texture_size.height as f32,
|
height: texture_size.height as f32,
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderPass for Renderer {
|
pub fn render(&self, encoder: &mut wgpu::CommandEncoder, render_target: &wgpu::TextureView) {
|
||||||
fn render(&self, encoder: &mut wgpu::CommandEncoder, render_target: &TextureView) {
|
|
||||||
// Draw the updated texture to the render target
|
// Draw the updated texture to the render target
|
||||||
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 {
|
||||||
|
@ -168,23 +157,20 @@ impl RenderPass for Renderer {
|
||||||
rpass.draw(0..6, 0..1);
|
rpass.draw(0..6, 0..1);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resize(&mut self, encoder: &mut wgpu::CommandEncoder, width: u32, height: u32) {
|
pub(crate) fn resize(
|
||||||
|
&mut self,
|
||||||
|
device: &mut wgpu::Device,
|
||||||
|
encoder: &mut wgpu::CommandEncoder,
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
) {
|
||||||
let matrix = ScalingMatrix::new((self.width, self.height), (width as f32, height as f32));
|
let matrix = ScalingMatrix::new((self.width, self.height), (width as f32, height as f32));
|
||||||
let transform_bytes = matrix.as_bytes();
|
let transform_bytes = matrix.as_bytes();
|
||||||
|
|
||||||
let temp_buf = self
|
let temp_buf =
|
||||||
.device
|
device.create_buffer_with_data(&transform_bytes, wgpu::BufferUsage::COPY_SRC);
|
||||||
.create_buffer_with_data(&transform_bytes, wgpu::BufferUsage::COPY_SRC);
|
|
||||||
encoder.copy_buffer_to_buffer(&temp_buf, 0, &self.uniform_buffer, 0, 64);
|
encoder.copy_buffer_to_buffer(&temp_buf, 0, &self.uniform_buffer, 0, 64);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We don't actually have to rebind the TextureView here.
|
|
||||||
// It's guaranteed that the initial texture never changes.
|
|
||||||
fn update_bindings(&mut self, _input_texture: &TextureView, _input_texture_size: &Extent3d) {}
|
|
||||||
|
|
||||||
fn debug(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, "{:?}", self)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
Loading…
Reference in a new issue