parent
f845d59213
commit
26ae25c3d8
|
@ -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
|
||||
|
|
149
src/builder.rs
149
src/builder.rs
|
@ -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,
|
||||
self.render_texture_format,
|
||||
&surface_size,
|
||||
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(
|
||||
// Create the backing texture
|
||||
let (scaling_matrix_inverse, texture_extent, texture, scaling_renderer, pixels_buffer_size) =
|
||||
create_backing_texture(
|
||||
&device,
|
||||
&texture_view,
|
||||
&texture_extent,
|
||||
// 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
|
||||
|
|
57
src/lib.rs
57
src/lib.rs
|
@ -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] {
|
||||
|
|
Loading…
Reference in a new issue