Resize buffer (#136)

- Fixes #125
This commit is contained in:
Josh 2021-02-28 23:05:28 +01:00 committed by GitHub
parent f845d59213
commit 26ae25c3d8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 176 additions and 73 deletions

View file

@ -26,10 +26,12 @@ struct World {
fn main() -> Result<(), Error> {
env_logger::init();
let mut width = WIDTH;
let mut height = HEIGHT;
let event_loop = EventLoop::new();
let mut input = WinitInputHelper::new();
let window = {
let size = LogicalSize::new(WIDTH as f64, HEIGHT as f64);
let size = LogicalSize::new(width as f64, height as f64);
WindowBuilder::new()
.with_title("Hello Pixels + Dear ImGui")
.with_inner_size(size)
@ -41,7 +43,7 @@ fn main() -> Result<(), Error> {
let mut pixels = {
let window_size = window.inner_size();
let surface_texture = SurfaceTexture::new(window_size.width, window_size.height, &window);
Pixels::new(WIDTH, HEIGHT, surface_texture)?
Pixels::new(width, height, surface_texture)?
};
let mut world = World::new();
@ -52,7 +54,7 @@ fn main() -> Result<(), Error> {
// Draw the current frame
if let Event::RedrawRequested(_) = event {
// Draw the world
world.draw(pixels.get_frame());
world.draw(pixels.get_frame(), width);
// Prepare Dear ImGui
gui.prepare(&window).expect("gui.prepare() failed");
@ -89,10 +91,13 @@ fn main() -> Result<(), Error> {
// Resize the window
if let Some(size) = input.window_resized() {
pixels.resize(size.width, size.height);
width = size.width;
height = size.height;
pixels.resize_buffer(width, height);
}
// Update internal state and request a redraw
world.update();
world.update(width, height);
window.request_redraw();
}
});
@ -110,12 +115,18 @@ impl World {
}
/// Update the `World` internal state; bounce the box around the screen.
fn update(&mut self) {
if self.box_x <= 0 || self.box_x + BOX_SIZE > WIDTH as i16 {
self.velocity_x *= -1;
fn update(&mut self, width: u32, height: u32) {
if self.box_x <= 0 {
self.velocity_x = 1;
}
if self.box_y <= 0 || self.box_y + BOX_SIZE > HEIGHT as i16 {
self.velocity_y *= -1;
if self.box_x + BOX_SIZE > width as i16 {
self.velocity_x = -1;
}
if self.box_y <= 0 {
self.velocity_y = 1;
}
if self.box_y + BOX_SIZE > height as i16 {
self.velocity_y = -1;
}
self.box_x += self.velocity_x;
@ -125,10 +136,10 @@ impl World {
/// Draw the `World` state to the frame buffer.
///
/// Assumes the default texture format: `wgpu::TextureFormat::Rgba8UnormSrgb`
fn draw(&self, frame: &mut [u8]) {
fn draw(&self, frame: &mut [u8], width: u32) {
for (i, pixel) in frame.chunks_exact_mut(4).enumerate() {
let x = (i % WIDTH as usize) as i16;
let y = (i / WIDTH as usize) as i16;
let x = (i % width as usize) as i16;
let y = (i / width as usize) as i16;
let inside_the_box = x >= self.box_x
&& x < self.box_x + BOX_SIZE

View file

@ -1,4 +1,5 @@
use crate::renderers::{ScalingMatrix, ScalingRenderer};
use crate::SurfaceSize;
use crate::{Error, Pixels, PixelsContext, SurfaceTexture};
use raw_window_handle::HasRawWindowHandle;
use std::env;
@ -184,66 +185,40 @@ impl<'req, 'win, W: HasRawWindowHandle> PixelsBuilder<'req, 'win, W> {
));
let adapter = pollster::block_on(adapter).ok_or(Error::AdapterNotFound)?;
let (device, queue) =
let (mut device, queue) =
pollster::block_on(adapter.request_device(&self.device_descriptor, None))
.map_err(Error::DeviceNotFound)?;
// The rest of this is technically a fixed-function pipeline... For now!
// Create a texture
let width = self.width;
let height = self.height;
let texture_extent = wgpu::Extent3d {
width,
height,
depth: 1,
};
let texture = device.create_texture(&wgpu::TextureDescriptor {
label: Some("pixels_source_texture"),
size: texture_extent,
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: self.texture_format,
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST,
});
let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default());
let texture_format_size = get_texture_format_size(self.texture_format);
// Create the pixel buffer
let capacity = ((width * height) as f32 * texture_format_size) as usize;
let mut pixels = Vec::with_capacity(capacity);
pixels.resize_with(capacity, Default::default);
let present_mode = self.present_mode;
// Create swap chain
let surface_size = self.surface_texture.size;
let swap_chain = device.create_swap_chain(
let swap_chain = create_swap_chain(
&mut device,
&surface,
&wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
format: self.render_texture_format,
width: surface_size.width,
height: surface_size.height,
present_mode,
},
);
let scaling_matrix_inverse = ScalingMatrix::new(
(width as f32, height as f32),
(surface_size.width as f32, surface_size.height as f32),
)
.transform
.inversed();
let scaling_renderer = ScalingRenderer::new(
&device,
&texture_view,
&texture_extent,
self.render_texture_format,
&surface_size,
present_mode,
);
// Create the backing texture
let (scaling_matrix_inverse, texture_extent, texture, scaling_renderer, pixels_buffer_size) =
create_backing_texture(
&device,
// Backing texture values
self.width,
self.height,
self.texture_format,
// Render texture values
&surface_size,
self.render_texture_format,
);
// Create the pixel buffer
let mut pixels = Vec::with_capacity(pixels_buffer_size);
pixels.resize_with(pixels_buffer_size, Default::default);
// Instantiate the Pixels struct
let context = PixelsContext {
device,
queue,
@ -251,7 +226,8 @@ impl<'req, 'win, W: HasRawWindowHandle> PixelsBuilder<'req, 'win, W> {
swap_chain,
texture,
texture_extent,
texture_format_size,
texture_format: self.texture_format,
texture_format_size: get_texture_format_size(self.texture_format),
scaling_renderer,
};
@ -266,7 +242,86 @@ impl<'req, 'win, W: HasRawWindowHandle> PixelsBuilder<'req, 'win, W> {
}
}
fn get_texture_format_size(texture_format: wgpu::TextureFormat) -> f32 {
pub(crate) fn create_swap_chain(
device: &mut wgpu::Device,
surface: &wgpu::Surface,
format: wgpu::TextureFormat,
surface_size: &SurfaceSize,
// width: u32,
// height: u32,
present_mode: wgpu::PresentMode,
) -> wgpu::SwapChain {
device.create_swap_chain(
&surface,
&wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
format,
width: surface_size.width,
height: surface_size.height,
present_mode,
},
)
}
pub(crate) fn create_backing_texture(
device: &wgpu::Device,
width: u32,
height: u32,
backing_texture_format: wgpu::TextureFormat,
surface_size: &SurfaceSize,
render_texture_format: wgpu::TextureFormat,
) -> (
ultraviolet::Mat4,
wgpu::Extent3d,
wgpu::Texture,
ScalingRenderer,
usize,
) {
let scaling_matrix_inverse = ScalingMatrix::new(
(width as f32, height as f32),
(surface_size.width as f32, surface_size.height as f32),
)
.transform
.inversed();
let texture_extent = wgpu::Extent3d {
width,
height,
depth: 1,
};
let texture = device.create_texture(&wgpu::TextureDescriptor {
label: Some("pixels_source_texture"),
size: texture_extent,
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: backing_texture_format,
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST,
});
let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default());
let scaling_renderer = ScalingRenderer::new(
device,
&texture_view,
&texture_extent,
render_texture_format,
);
let texture_format_size = get_texture_format_size(backing_texture_format);
let pixels_buffer_size = ((width * height) as f32 * texture_format_size) as usize;
(
scaling_matrix_inverse,
texture_extent,
texture,
scaling_renderer,
pixels_buffer_size,
)
}
#[inline]
const fn get_texture_format_size(texture_format: wgpu::TextureFormat) -> f32 {
match texture_format {
// 8-bit formats
wgpu::TextureFormat::R8Unorm

View file

@ -74,6 +74,7 @@ pub struct PixelsContext {
/// Provides access to the texture size.
pub texture_extent: wgpu::Extent3d,
pub texture_format: wgpu::TextureFormat,
/// Defines the "data rate" for the raw texture data. This is effectively the "bytes per pixel"
/// count.
@ -182,6 +183,31 @@ impl Pixels {
PixelsBuilder::new(width, height, surface_texture).build()
}
/// Resize the pixel buffer, this doesn't resize the surface upon which the pixel buffer is rendered.
pub fn resize_buffer(&mut self, width: u32, height: u32) {
// Recreate the backing texture
let (scaling_matrix_inverse, texture_extent, texture, scaling_renderer, pixels_buffer_size) =
builder::create_backing_texture(
&self.context.device,
// Backing texture values
width,
height,
self.context.texture_format,
// Render texture values
&self.surface_size,
self.render_texture_format,
);
self.scaling_matrix_inverse = scaling_matrix_inverse;
self.context.texture_extent = texture_extent;
self.context.texture = texture;
self.context.scaling_renderer = scaling_renderer;
// Resize the pixel buffer
self.pixels
.resize_with(pixels_buffer_size, Default::default);
}
/// Resize the surface upon which the pixel buffer is rendered.
///
/// This does not resize the pixel buffer. The pixel buffer will be fit onto the surface as
@ -206,16 +232,7 @@ impl Pixels {
.inversed();
// Recreate the swap chain
self.context.swap_chain = self.context.device.create_swap_chain(
&self.context.surface,
&wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
format: self.render_texture_format,
width: self.surface_size.width,
height: self.surface_size.height,
present_mode: self.present_mode,
},
);
self.recreate_swap_chain();
// Update state for all render passes
self.context
@ -300,6 +317,15 @@ impl Pixels {
.context
.swap_chain
.get_current_frame()
.or_else(|err| match err {
wgpu::SwapChainError::Outdated => {
// Recreate the swap chain to mitigate race condition on drawing surface resize.
// See https://github.com/parasyte/pixels/issues/121
self.recreate_swap_chain();
self.context.swap_chain.get_current_frame()
}
err => Err(err),
})
.map_err(Error::Swapchain)?;
let mut encoder =
self.context
@ -333,6 +359,17 @@ impl Pixels {
Ok(())
}
// Re-create the swap chain with its own values
pub(crate) fn recreate_swap_chain(&mut self) {
self.context.swap_chain = builder::create_swap_chain(
&mut self.context.device,
&self.context.surface,
self.render_texture_format,
&self.surface_size,
self.present_mode,
);
}
/// 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.
pub fn get_frame(&mut self) -> &mut [u8] {