Fix invalid texture sizes (#250)
- Makes methods fallible when they create textures. - Correctly handle window resize in fltk example. - TODO: tests - Closes #240
This commit is contained in:
parent
b4fc48c740
commit
6f4fa6c967
|
@ -45,11 +45,8 @@ fn main() -> Result<(), Error> {
|
||||||
// The one and only event that winit_input_helper doesn't have for us...
|
// The one and only event that winit_input_helper doesn't have for us...
|
||||||
if let Event::RedrawRequested(_) = event {
|
if let Event::RedrawRequested(_) = event {
|
||||||
life.draw(pixels.get_frame_mut());
|
life.draw(pixels.get_frame_mut());
|
||||||
if pixels
|
if let Err(err) = pixels.render() {
|
||||||
.render()
|
error!("pixels.render() failed: {}", err);
|
||||||
.map_err(|e| error!("pixels.render() failed: {}", e))
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
*control_flow = ControlFlow::Exit;
|
*control_flow = ControlFlow::Exit;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -98,17 +95,17 @@ fn main() -> Result<(), Error> {
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
if input.mouse_pressed(0) {
|
if input.mouse_pressed(0) {
|
||||||
debug!("Mouse click at {:?}", mouse_cell);
|
debug!("Mouse click at {mouse_cell:?}");
|
||||||
draw_state = Some(life.toggle(mouse_cell.0, mouse_cell.1));
|
draw_state = Some(life.toggle(mouse_cell.0, mouse_cell.1));
|
||||||
} else if let Some(draw_alive) = draw_state {
|
} else if let Some(draw_alive) = draw_state {
|
||||||
let release = input.mouse_released(0);
|
let release = input.mouse_released(0);
|
||||||
let held = input.mouse_held(0);
|
let held = input.mouse_held(0);
|
||||||
debug!("Draw at {:?} => {:?}", mouse_prev_cell, mouse_cell);
|
debug!("Draw at {mouse_prev_cell:?} => {mouse_cell:?}");
|
||||||
debug!("Mouse held {:?}, release {:?}", held, release);
|
debug!("Mouse held {held:?}, release {release:?}");
|
||||||
// If they either released (finishing the drawing) or are still
|
// If they either released (finishing the drawing) or are still
|
||||||
// in the middle of drawing, keep going.
|
// in the middle of drawing, keep going.
|
||||||
if release || held {
|
if release || held {
|
||||||
debug!("Draw line of {:?}", draw_alive);
|
debug!("Draw line of {draw_alive:?}");
|
||||||
life.set_line(
|
life.set_line(
|
||||||
mouse_prev_cell.0,
|
mouse_prev_cell.0,
|
||||||
mouse_prev_cell.1,
|
mouse_prev_cell.1,
|
||||||
|
@ -125,7 +122,11 @@ fn main() -> Result<(), Error> {
|
||||||
}
|
}
|
||||||
// Resize the window
|
// Resize the window
|
||||||
if let Some(size) = input.window_resized() {
|
if let Some(size) = input.window_resized() {
|
||||||
pixels.resize_surface(size.width, size.height);
|
if let Err(err) = pixels.resize_surface(size.width, size.height) {
|
||||||
|
error!("pixels.resize_surface() failed: {err}");
|
||||||
|
*control_flow = ControlFlow::Exit;
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if !paused || input.key_pressed_os(VirtualKeyCode::Space) {
|
if !paused || input.key_pressed_os(VirtualKeyCode::Space) {
|
||||||
life.update();
|
life.update();
|
||||||
|
|
|
@ -45,7 +45,7 @@ fn main() -> Result<(), Error> {
|
||||||
};
|
};
|
||||||
let mut world = World::new();
|
let mut world = World::new();
|
||||||
let mut time = 0.0;
|
let mut time = 0.0;
|
||||||
let mut noise_renderer = NoiseRenderer::new(&pixels, window_size.width, window_size.height);
|
let mut noise_renderer = NoiseRenderer::new(&pixels, window_size.width, window_size.height)?;
|
||||||
|
|
||||||
event_loop.run(move |event, _, control_flow| {
|
event_loop.run(move |event, _, control_flow| {
|
||||||
// Draw the current frame
|
// Draw the current frame
|
||||||
|
@ -64,10 +64,8 @@ fn main() -> Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
|
|
||||||
if render_result
|
if let Err(err) = render_result {
|
||||||
.map_err(|e| error!("pixels.render_with() failed: {}", e))
|
error!("pixels.render_with() failed: {err}");
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
*control_flow = ControlFlow::Exit;
|
*control_flow = ControlFlow::Exit;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -83,8 +81,16 @@ fn main() -> Result<(), Error> {
|
||||||
|
|
||||||
// Resize the window
|
// Resize the window
|
||||||
if let Some(size) = input.window_resized() {
|
if let Some(size) = input.window_resized() {
|
||||||
pixels.resize_surface(size.width, size.height);
|
if let Err(err) = pixels.resize_surface(size.width, size.height) {
|
||||||
noise_renderer.resize(&pixels, size.width, size.height);
|
error!("pixels.resize_surface() failed: {err}");
|
||||||
|
*control_flow = ControlFlow::Exit;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if let Err(err) = noise_renderer.resize(&pixels, size.width, size.height) {
|
||||||
|
error!("noise_renderer.resize() failed: {err}");
|
||||||
|
*control_flow = ControlFlow::Exit;
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update internal state and request a redraw
|
// Update internal state and request a redraw
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
use pixels::wgpu::{self, util::DeviceExt};
|
use pixels::{
|
||||||
|
check_texture_size,
|
||||||
|
wgpu::{self, util::DeviceExt},
|
||||||
|
TextureError,
|
||||||
|
};
|
||||||
|
|
||||||
pub(crate) struct NoiseRenderer {
|
pub(crate) struct NoiseRenderer {
|
||||||
texture_view: wgpu::TextureView,
|
texture_view: wgpu::TextureView,
|
||||||
|
@ -11,14 +15,18 @@ pub(crate) struct NoiseRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NoiseRenderer {
|
impl NoiseRenderer {
|
||||||
pub(crate) fn new(pixels: &pixels::Pixels, width: u32, height: u32) -> Self {
|
pub(crate) fn new(
|
||||||
|
pixels: &pixels::Pixels,
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
) -> Result<Self, TextureError> {
|
||||||
let device = pixels.device();
|
let device = pixels.device();
|
||||||
let shader = wgpu::include_wgsl!("../shaders/noise.wgsl");
|
let shader = wgpu::include_wgsl!("../shaders/noise.wgsl");
|
||||||
let module = device.create_shader_module(shader);
|
let module = device.create_shader_module(shader);
|
||||||
|
|
||||||
// Create a texture view that will be used as input
|
// Create a texture view that will be used as input
|
||||||
// This will be used as the render target for the default scaling renderer
|
// This will be used as the render target for the default scaling renderer
|
||||||
let texture_view = create_texture_view(pixels, width, height);
|
let texture_view = create_texture_view(pixels, width, height)?;
|
||||||
|
|
||||||
// Create a texture sampler with nearest neighbor
|
// Create a texture sampler with nearest neighbor
|
||||||
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
|
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
|
||||||
|
@ -139,7 +147,7 @@ impl NoiseRenderer {
|
||||||
multiview: None,
|
multiview: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
Self {
|
Ok(Self {
|
||||||
texture_view,
|
texture_view,
|
||||||
sampler,
|
sampler,
|
||||||
bind_group_layout,
|
bind_group_layout,
|
||||||
|
@ -147,19 +155,20 @@ impl NoiseRenderer {
|
||||||
render_pipeline,
|
render_pipeline,
|
||||||
time_buffer,
|
time_buffer,
|
||||||
vertex_buffer,
|
vertex_buffer,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_texture_view(&self) -> &wgpu::TextureView {
|
pub(crate) fn get_texture_view(&self) -> &wgpu::TextureView {
|
||||||
&self.texture_view
|
&self.texture_view
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn resize(&mut self, pixels: &pixels::Pixels, width: u32, height: u32) {
|
pub(crate) fn resize(
|
||||||
if width == 0 || height == 0 {
|
&mut self,
|
||||||
return;
|
pixels: &pixels::Pixels,
|
||||||
}
|
width: u32,
|
||||||
|
height: u32,
|
||||||
self.texture_view = create_texture_view(pixels, width, height);
|
) -> Result<(), TextureError> {
|
||||||
|
self.texture_view = create_texture_view(pixels, width, height)?;
|
||||||
self.bind_group = create_bind_group(
|
self.bind_group = create_bind_group(
|
||||||
pixels.device(),
|
pixels.device(),
|
||||||
&self.bind_group_layout,
|
&self.bind_group_layout,
|
||||||
|
@ -167,6 +176,8 @@ impl NoiseRenderer {
|
||||||
&self.sampler,
|
&self.sampler,
|
||||||
&self.time_buffer,
|
&self.time_buffer,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn update(&self, queue: &wgpu::Queue, time: f32) {
|
pub(crate) fn update(&self, queue: &wgpu::Queue, time: f32) {
|
||||||
|
@ -199,8 +210,13 @@ impl NoiseRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_texture_view(pixels: &pixels::Pixels, width: u32, height: u32) -> wgpu::TextureView {
|
fn create_texture_view(
|
||||||
|
pixels: &pixels::Pixels,
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
) -> Result<wgpu::TextureView, TextureError> {
|
||||||
let device = pixels.device();
|
let device = pixels.device();
|
||||||
|
check_texture_size(device, width, height)?;
|
||||||
let texture_descriptor = wgpu::TextureDescriptor {
|
let texture_descriptor = wgpu::TextureDescriptor {
|
||||||
label: None,
|
label: None,
|
||||||
size: pixels::wgpu::Extent3d {
|
size: pixels::wgpu::Extent3d {
|
||||||
|
@ -215,9 +231,9 @@ fn create_texture_view(pixels: &pixels::Pixels, width: u32, height: u32) -> wgpu
|
||||||
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::RENDER_ATTACHMENT,
|
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||||
};
|
};
|
||||||
|
|
||||||
device
|
Ok(device
|
||||||
.create_texture(&texture_descriptor)
|
.create_texture(&texture_descriptor)
|
||||||
.create_view(&wgpu::TextureViewDescriptor::default())
|
.create_view(&wgpu::TextureViewDescriptor::default()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_bind_group(
|
fn create_bind_group(
|
||||||
|
|
|
@ -76,10 +76,8 @@ fn main() -> Result<(), Error> {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Basic error handling
|
// Basic error handling
|
||||||
if render_result
|
if let Err(err) = render_result {
|
||||||
.map_err(|e| error!("pixels.render() failed: {}", e))
|
error!("pixels.render() failed: {err}");
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
*control_flow = ControlFlow::Exit;
|
*control_flow = ControlFlow::Exit;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -103,12 +101,20 @@ fn main() -> Result<(), Error> {
|
||||||
if let Some(size) = input.window_resized() {
|
if let Some(size) = input.window_resized() {
|
||||||
if size.width > 0 && size.height > 0 {
|
if size.width > 0 && size.height > 0 {
|
||||||
// Resize the surface texture
|
// Resize the surface texture
|
||||||
pixels.resize_surface(size.width, size.height);
|
if let Err(err) = pixels.resize_surface(size.width, size.height) {
|
||||||
|
error!("pixels.resize_surface() failed: {err}");
|
||||||
|
*control_flow = ControlFlow::Exit;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Resize the world
|
// Resize the world
|
||||||
let LogicalSize { width, height } = size.to_logical(scale_factor);
|
let LogicalSize { width, height } = size.to_logical(scale_factor);
|
||||||
world.resize(width, height);
|
world.resize(width, height);
|
||||||
pixels.resize_buffer(width, height);
|
if let Err(err) = pixels.resize_buffer(width, height) {
|
||||||
|
error!("pixels.resize_buffer() failed: {err}");
|
||||||
|
*control_flow = ControlFlow::Exit;
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -140,8 +140,8 @@ fn main() -> Result<(), Error> {
|
||||||
move |g| {
|
move |g| {
|
||||||
// Drawing
|
// Drawing
|
||||||
g.game.world.draw(g.game.pixels.get_frame_mut());
|
g.game.world.draw(g.game.pixels.get_frame_mut());
|
||||||
if let Err(e) = g.game.pixels.render() {
|
if let Err(err) = g.game.pixels.render() {
|
||||||
error!("pixels.render() failed: {}", e);
|
error!("pixels.render() failed: {err}");
|
||||||
g.exit();
|
g.exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,7 +166,10 @@ fn main() -> Result<(), Error> {
|
||||||
|
|
||||||
// Resize the window
|
// Resize the window
|
||||||
if let Some(size) = g.game.input.window_resized() {
|
if let Some(size) = g.game.input.window_resized() {
|
||||||
g.game.pixels.resize_surface(size.width, size.height);
|
if let Err(err) = g.game.pixels.resize_surface(size.width, size.height) {
|
||||||
|
error!("pixels.resize_surface() failed: {err}");
|
||||||
|
g.exit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -71,7 +71,11 @@ fn main() -> Result<(), Error> {
|
||||||
|
|
||||||
// Resize the window
|
// Resize the window
|
||||||
if let Some(size) = input.window_resized() {
|
if let Some(size) = input.window_resized() {
|
||||||
pixels.resize_surface(size.width, size.height);
|
if let Err(err) = pixels.resize_surface(size.width, size.height) {
|
||||||
|
error!("pixels.resize_surface() failed: {err}");
|
||||||
|
*control_flow = ControlFlow::Exit;
|
||||||
|
return;
|
||||||
|
}
|
||||||
framework.resize(size.width, size.height);
|
framework.resize(size.width, size.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,10 +109,8 @@ fn main() -> Result<(), Error> {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Basic error handling
|
// Basic error handling
|
||||||
if render_result
|
if let Err(err) = render_result {
|
||||||
.map_err(|e| error!("pixels.render() failed: {}", e))
|
error!("pixels.render() failed: {err}");
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
*control_flow = ControlFlow::Exit;
|
*control_flow = ControlFlow::Exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,10 @@
|
||||||
#![forbid(unsafe_code)]
|
#![forbid(unsafe_code)]
|
||||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||||
|
|
||||||
use fltk::{app, enums::Event, prelude::*, window::Window};
|
use fltk::{app, prelude::*, window::Window};
|
||||||
use log::error;
|
use log::error;
|
||||||
use pixels::{Error, Pixels, SurfaceTexture};
|
use pixels::{Error, Pixels, SurfaceTexture};
|
||||||
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
const WIDTH: u32 = 600;
|
const WIDTH: u32 = 600;
|
||||||
const HEIGHT: u32 = 400;
|
const HEIGHT: u32 = 400;
|
||||||
|
@ -26,6 +27,7 @@ fn main() -> Result<(), Error> {
|
||||||
let mut win = Window::default()
|
let mut win = Window::default()
|
||||||
.with_size(WIDTH as i32, HEIGHT as i32)
|
.with_size(WIDTH as i32, HEIGHT as i32)
|
||||||
.with_label("Hello Pixels");
|
.with_label("Hello Pixels");
|
||||||
|
win.make_resizable(true);
|
||||||
win.end();
|
win.end();
|
||||||
win.show();
|
win.show();
|
||||||
|
|
||||||
|
@ -33,29 +35,39 @@ fn main() -> Result<(), Error> {
|
||||||
let pixel_width = win.pixel_w() as u32;
|
let pixel_width = win.pixel_w() as u32;
|
||||||
let pixel_height = win.pixel_h() as u32;
|
let pixel_height = win.pixel_h() as u32;
|
||||||
let surface_texture = SurfaceTexture::new(pixel_width, pixel_height, &win);
|
let surface_texture = SurfaceTexture::new(pixel_width, pixel_height, &win);
|
||||||
|
|
||||||
Pixels::new(WIDTH, HEIGHT, surface_texture)?
|
Pixels::new(WIDTH, HEIGHT, surface_texture)?
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut world = World::new();
|
let mut world = World::new();
|
||||||
|
|
||||||
while app.wait() {
|
// Handle resize events
|
||||||
// Handle window events
|
let surface_size = Rc::new(RefCell::new(None));
|
||||||
if app::event() == Event::Resize {
|
let surface_resize = surface_size.clone();
|
||||||
let pixel_width = win.pixel_w() as u32;
|
win.resize_callback(move |win, _x, _y, width, height| {
|
||||||
let pixel_height = win.pixel_h() as u32;
|
let scale_factor = win.pixels_per_unit();
|
||||||
pixels.resize_surface(pixel_width, pixel_height);
|
let width = (width as f32 * scale_factor) as u32;
|
||||||
}
|
let height = (height as f32 * scale_factor) as u32;
|
||||||
|
|
||||||
|
surface_resize.borrow_mut().replace((width, height));
|
||||||
|
});
|
||||||
|
|
||||||
|
while app.wait() {
|
||||||
// Update internal state
|
// Update internal state
|
||||||
world.update();
|
world.update();
|
||||||
|
|
||||||
|
// Resize the window
|
||||||
|
if let Some((width, height)) = surface_size.borrow_mut().take() {
|
||||||
|
if let Err(err) = pixels.resize_surface(width, height) {
|
||||||
|
error!("pixels.resize_surface() failed: {err}");
|
||||||
|
app.quit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Draw the current frame
|
// Draw the current frame
|
||||||
world.draw(pixels.get_frame_mut());
|
world.draw(pixels.get_frame_mut());
|
||||||
if pixels
|
if let Err(err) = pixels.render() {
|
||||||
.render()
|
error!("pixels.render() failed: {err}");
|
||||||
.map_err(|e| error!("pixels.render() failed: {}", e))
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
app.quit();
|
app.quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -67,7 +67,10 @@ fn main() -> Result<(), Error> {
|
||||||
|
|
||||||
// Resize the window
|
// Resize the window
|
||||||
WindowEvent::Resized(size) => {
|
WindowEvent::Resized(size) => {
|
||||||
pixels.resize_surface(size.width, size.height);
|
if let Err(err) = pixels.resize_surface(size.width, size.height) {
|
||||||
|
error!("pixels.resize_surface() failed: {err}");
|
||||||
|
*control_flow = ControlFlow::Exit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => {}
|
_ => {}
|
||||||
|
@ -82,11 +85,8 @@ fn main() -> Result<(), Error> {
|
||||||
// Draw the current frame
|
// Draw the current frame
|
||||||
Event::RedrawRequested(_) => {
|
Event::RedrawRequested(_) => {
|
||||||
world.draw(pixels.get_frame_mut());
|
world.draw(pixels.get_frame_mut());
|
||||||
if pixels
|
if let Err(err) = pixels.render() {
|
||||||
.render()
|
error!("pixels.render() failed: {err}");
|
||||||
.map_err(|e| error!("pixels.render() failed: {}", e))
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
*control_flow = ControlFlow::Exit;
|
*control_flow = ControlFlow::Exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,11 +111,8 @@ async fn run() {
|
||||||
// Draw the current frame
|
// Draw the current frame
|
||||||
if let Event::RedrawRequested(_) = event {
|
if let Event::RedrawRequested(_) = event {
|
||||||
world.draw(pixels.get_frame_mut());
|
world.draw(pixels.get_frame_mut());
|
||||||
if pixels
|
if let Err(err) = pixels.render() {
|
||||||
.render()
|
error!("pixels.render() failed: {err}");
|
||||||
.map_err(|e| error!("pixels.render() failed: {}", e))
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
*control_flow = ControlFlow::Exit;
|
*control_flow = ControlFlow::Exit;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -131,7 +128,11 @@ async fn run() {
|
||||||
|
|
||||||
// Resize the window
|
// Resize the window
|
||||||
if let Some(size) = input.window_resized() {
|
if let Some(size) = input.window_resized() {
|
||||||
pixels.resize_surface(size.width, size.height);
|
if let Err(err) = pixels.resize_surface(size.width, size.height) {
|
||||||
|
error!("pixels.resize_surface() failed: {err}");
|
||||||
|
*control_flow = ControlFlow::Exit;
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update internal state and request a redraw
|
// Update internal state and request a redraw
|
||||||
|
|
|
@ -46,11 +46,8 @@ fn main() -> Result<(), Error> {
|
||||||
// Draw the current frame
|
// Draw the current frame
|
||||||
if let Event::RedrawRequested(_) = event {
|
if let Event::RedrawRequested(_) = event {
|
||||||
world.draw(pixels.get_frame_mut());
|
world.draw(pixels.get_frame_mut());
|
||||||
if pixels
|
if let Err(err) = pixels.render() {
|
||||||
.render()
|
error!("pixels.render() failed: {err}");
|
||||||
.map_err(|e| error!("pixels.render() failed: {}", e))
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
*control_flow = ControlFlow::Exit;
|
*control_flow = ControlFlow::Exit;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -66,7 +63,11 @@ fn main() -> Result<(), Error> {
|
||||||
|
|
||||||
// Resize the window
|
// Resize the window
|
||||||
if let Some(size) = input.window_resized() {
|
if let Some(size) = input.window_resized() {
|
||||||
pixels.resize_surface(size.width, size.height);
|
if let Err(err) = pixels.resize_surface(size.width, size.height) {
|
||||||
|
error!("pixels.resize_surface() failed: {err}");
|
||||||
|
*control_flow = ControlFlow::Exit;
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update internal state and request a redraw
|
// Update internal state and request a redraw
|
||||||
|
|
|
@ -56,11 +56,8 @@ fn main() -> Result<(), Error> {
|
||||||
dst[3] = (src >> 24) as u8;
|
dst[3] = (src >> 24) as u8;
|
||||||
}
|
}
|
||||||
|
|
||||||
if pixels
|
if let Err(err) = pixels.render() {
|
||||||
.render()
|
error!("pixels.render() failed: {err}");
|
||||||
.map_err(|e| error!("pixels.render() failed: {}", e))
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
*control_flow = ControlFlow::Exit;
|
*control_flow = ControlFlow::Exit;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -76,7 +73,11 @@ fn main() -> Result<(), Error> {
|
||||||
|
|
||||||
// Resize the window
|
// Resize the window
|
||||||
if let Some(size) = input.window_resized() {
|
if let Some(size) = input.window_resized() {
|
||||||
pixels.resize_surface(size.width, size.height);
|
if let Err(err) = pixels.resize_surface(size.width, size.height) {
|
||||||
|
error!("pixels.resize_surface() failed: {err}");
|
||||||
|
*control_flow = ControlFlow::Exit;
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update internal state and request a redraw
|
// Update internal state and request a redraw
|
||||||
|
|
|
@ -44,11 +44,8 @@ fn main() -> Result<(), Error> {
|
||||||
// Draw the current frame
|
// Draw the current frame
|
||||||
if let Event::RedrawRequested(_) = event {
|
if let Event::RedrawRequested(_) = event {
|
||||||
pixels.get_frame_mut().copy_from_slice(drawing.data());
|
pixels.get_frame_mut().copy_from_slice(drawing.data());
|
||||||
if pixels
|
if let Err(err) = pixels.render() {
|
||||||
.render()
|
error!("pixels.render() failed: {err}");
|
||||||
.map_err(|e| error!("pixels.render() failed: {}", e))
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
*control_flow = ControlFlow::Exit;
|
*control_flow = ControlFlow::Exit;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -64,7 +61,11 @@ fn main() -> Result<(), Error> {
|
||||||
|
|
||||||
// Resize the window
|
// Resize the window
|
||||||
if let Some(size) = input.window_resized() {
|
if let Some(size) = input.window_resized() {
|
||||||
pixels.resize_surface(size.width, size.height);
|
if let Err(err) = pixels.resize_surface(size.width, size.height) {
|
||||||
|
error!("pixels.resize_surface() failed: {err}");
|
||||||
|
*control_flow = ControlFlow::Exit;
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update internal state and request a redraw
|
// Update internal state and request a redraw
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::renderers::{ScalingMatrix, ScalingRenderer};
|
use crate::renderers::{ScalingMatrix, ScalingRenderer};
|
||||||
use crate::SurfaceSize;
|
use crate::{Error, Pixels, PixelsContext, SurfaceSize, SurfaceTexture, TextureError};
|
||||||
use crate::{Error, Pixels, PixelsContext, SurfaceTexture};
|
|
||||||
use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
|
use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
|
||||||
|
|
||||||
/// A builder to help create customized pixel buffers.
|
/// A builder to help create customized pixel buffers.
|
||||||
|
@ -321,7 +320,7 @@ impl<'req, 'dev, 'win, W: HasRawWindowHandle + HasRawDisplayHandle>
|
||||||
render_texture_format,
|
render_texture_format,
|
||||||
clear_color,
|
clear_color,
|
||||||
blend_state,
|
blend_state,
|
||||||
);
|
)?;
|
||||||
|
|
||||||
// Create the pixel buffer
|
// Create the pixel buffer
|
||||||
let mut pixels = Vec::with_capacity(pixels_buffer_size);
|
let mut pixels = Vec::with_capacity(pixels_buffer_size);
|
||||||
|
@ -397,6 +396,28 @@ impl<'req, 'dev, 'win, W: HasRawWindowHandle + HasRawDisplayHandle>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Compare the given size to the limits defined by `device`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// - [`TextureError::TextureWidth`] when `width` is 0 or greater than GPU texture limits.
|
||||||
|
/// - [`TextureError::TextureHeight`] when `height` is 0 or greater than GPU texture limits.
|
||||||
|
pub fn check_texture_size(
|
||||||
|
device: &wgpu::Device,
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
) -> Result<(), TextureError> {
|
||||||
|
let limits = device.limits();
|
||||||
|
if width == 0 || width > limits.max_texture_dimension_2d {
|
||||||
|
return Err(TextureError::TextureWidth(width));
|
||||||
|
}
|
||||||
|
if height == 0 || height > limits.max_texture_dimension_2d {
|
||||||
|
return Err(TextureError::TextureHeight(height));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub(crate) fn create_backing_texture(
|
pub(crate) fn create_backing_texture(
|
||||||
device: &wgpu::Device,
|
device: &wgpu::Device,
|
||||||
|
@ -407,13 +428,18 @@ pub(crate) fn create_backing_texture(
|
||||||
render_texture_format: wgpu::TextureFormat,
|
render_texture_format: wgpu::TextureFormat,
|
||||||
clear_color: wgpu::Color,
|
clear_color: wgpu::Color,
|
||||||
blend_state: wgpu::BlendState,
|
blend_state: wgpu::BlendState,
|
||||||
) -> (
|
) -> Result<
|
||||||
|
(
|
||||||
ultraviolet::Mat4,
|
ultraviolet::Mat4,
|
||||||
wgpu::Extent3d,
|
wgpu::Extent3d,
|
||||||
wgpu::Texture,
|
wgpu::Texture,
|
||||||
ScalingRenderer,
|
ScalingRenderer,
|
||||||
usize,
|
usize,
|
||||||
) {
|
),
|
||||||
|
TextureError,
|
||||||
|
> {
|
||||||
|
check_texture_size(device, width, height)?;
|
||||||
|
|
||||||
let scaling_matrix_inverse = ScalingMatrix::new(
|
let scaling_matrix_inverse = ScalingMatrix::new(
|
||||||
(width as f32, height as f32),
|
(width as f32, height as f32),
|
||||||
(surface_size.width as f32, surface_size.height as f32),
|
(surface_size.width as f32, surface_size.height as f32),
|
||||||
|
@ -451,13 +477,13 @@ pub(crate) fn create_backing_texture(
|
||||||
let texture_format_size = get_texture_format_size(backing_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;
|
let pixels_buffer_size = ((width * height) as f32 * texture_format_size) as usize;
|
||||||
|
|
||||||
(
|
Ok((
|
||||||
scaling_matrix_inverse,
|
scaling_matrix_inverse,
|
||||||
texture_extent,
|
texture_extent,
|
||||||
texture,
|
texture,
|
||||||
scaling_renderer,
|
scaling_renderer,
|
||||||
pixels_buffer_size,
|
pixels_buffer_size,
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
|
|
45
src/lib.rs
45
src/lib.rs
|
@ -31,7 +31,7 @@
|
||||||
|
|
||||||
#![deny(clippy::all)]
|
#![deny(clippy::all)]
|
||||||
|
|
||||||
pub use crate::builder::PixelsBuilder;
|
pub use crate::builder::{check_texture_size, PixelsBuilder};
|
||||||
pub use crate::renderers::ScalingRenderer;
|
pub use crate::renderers::ScalingRenderer;
|
||||||
pub use raw_window_handle;
|
pub use raw_window_handle;
|
||||||
use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
|
use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
|
||||||
|
@ -111,6 +111,7 @@ pub struct Pixels {
|
||||||
|
|
||||||
/// All the ways in which creating a pixel buffer can fail.
|
/// All the ways in which creating a pixel buffer can fail.
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
|
#[non_exhaustive]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// No suitable [`wgpu::Adapter`] found
|
/// No suitable [`wgpu::Adapter`] found
|
||||||
#[error("No suitable `wgpu::Adapter` found.")]
|
#[error("No suitable `wgpu::Adapter` found.")]
|
||||||
|
@ -121,6 +122,9 @@ pub enum Error {
|
||||||
/// Equivalent to [`wgpu::SurfaceError`]
|
/// Equivalent to [`wgpu::SurfaceError`]
|
||||||
#[error("The GPU failed to acquire a surface frame.")]
|
#[error("The GPU failed to acquire a surface frame.")]
|
||||||
Surface(wgpu::SurfaceError),
|
Surface(wgpu::SurfaceError),
|
||||||
|
/// Equivalent to [`TextureError`]
|
||||||
|
#[error("Texture creation failed: {0}")]
|
||||||
|
InvalidTexture(#[from] TextureError),
|
||||||
/// User-defined error from custom render function
|
/// User-defined error from custom render function
|
||||||
#[error("User-defined error.")]
|
#[error("User-defined error.")]
|
||||||
UserDefined(#[from] DynError),
|
UserDefined(#[from] DynError),
|
||||||
|
@ -128,6 +132,18 @@ pub enum Error {
|
||||||
|
|
||||||
type DynError = Box<dyn std::error::Error + Send + Sync + 'static>;
|
type DynError = Box<dyn std::error::Error + Send + Sync + 'static>;
|
||||||
|
|
||||||
|
/// All the ways in which creating a texture can fail.
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum TextureError {
|
||||||
|
/// Unable to create a backing texture; Width is either 0 or greater than GPU limits
|
||||||
|
#[error("Texture width is invalid: {0}")]
|
||||||
|
TextureWidth(u32),
|
||||||
|
/// Unable to create a backing texture; Height is either 0 or greater than GPU limits
|
||||||
|
#[error("Texture height is invalid: {0}")]
|
||||||
|
TextureHeight(u32),
|
||||||
|
}
|
||||||
|
|
||||||
impl<'win, W: HasRawWindowHandle + HasRawDisplayHandle> SurfaceTexture<'win, W> {
|
impl<'win, W: HasRawWindowHandle + HasRawDisplayHandle> SurfaceTexture<'win, W> {
|
||||||
/// Create a logical texture for a window surface.
|
/// Create a logical texture for a window surface.
|
||||||
///
|
///
|
||||||
|
@ -268,13 +284,11 @@ impl Pixels {
|
||||||
/// Call this method to change the virtual screen resolution. E.g. when you want your pixel
|
/// Call this method to change the virtual screen resolution. E.g. when you want your pixel
|
||||||
/// buffer to be resized from `640x480` to `800x600`.
|
/// buffer to be resized from `640x480` to `800x600`.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// Panics when `width` or `height` are 0.
|
/// - [`TextureError::TextureWidth`] when `width` is 0 or greater than GPU texture limits.
|
||||||
pub fn resize_buffer(&mut self, width: u32, height: u32) {
|
/// - [`TextureError::TextureHeight`] when `height` is 0 or greater than GPU texture limits.
|
||||||
assert!(width > 0);
|
pub fn resize_buffer(&mut self, width: u32, height: u32) -> Result<(), TextureError> {
|
||||||
assert!(height > 0);
|
|
||||||
|
|
||||||
// Recreate the backing texture
|
// Recreate the backing texture
|
||||||
let (scaling_matrix_inverse, texture_extent, texture, scaling_renderer, pixels_buffer_size) =
|
let (scaling_matrix_inverse, texture_extent, texture, scaling_renderer, pixels_buffer_size) =
|
||||||
builder::create_backing_texture(
|
builder::create_backing_texture(
|
||||||
|
@ -288,7 +302,7 @@ impl Pixels {
|
||||||
self.render_texture_format,
|
self.render_texture_format,
|
||||||
self.context.scaling_renderer.clear_color,
|
self.context.scaling_renderer.clear_color,
|
||||||
self.blend_state,
|
self.blend_state,
|
||||||
);
|
)?;
|
||||||
|
|
||||||
self.scaling_matrix_inverse = scaling_matrix_inverse;
|
self.scaling_matrix_inverse = scaling_matrix_inverse;
|
||||||
self.context.texture_extent = texture_extent;
|
self.context.texture_extent = texture_extent;
|
||||||
|
@ -298,6 +312,8 @@ impl Pixels {
|
||||||
// Resize the pixel buffer
|
// Resize the pixel buffer
|
||||||
self.pixels
|
self.pixels
|
||||||
.resize_with(pixels_buffer_size, Default::default);
|
.resize_with(pixels_buffer_size, Default::default);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resize the surface upon which the pixel buffer texture is rendered.
|
/// Resize the surface upon which the pixel buffer texture is rendered.
|
||||||
|
@ -311,10 +327,13 @@ 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. Does nothing when `width` or `height` are 0.
|
/// is in physical pixel units. Does nothing when `width` or `height` are 0.
|
||||||
pub fn resize_surface(&mut self, width: u32, height: u32) {
|
///
|
||||||
if width == 0 || height == 0 {
|
/// # Errors
|
||||||
return;
|
///
|
||||||
}
|
/// - [`TextureError::TextureWidth`] when `width` is 0 or greater than GPU texture limits.
|
||||||
|
/// - [`TextureError::TextureHeight`] when `height` is 0 or greater than GPU texture limits.
|
||||||
|
pub fn resize_surface(&mut self, width: u32, height: u32) -> Result<(), TextureError> {
|
||||||
|
check_texture_size(&self.context.device, width, height)?;
|
||||||
|
|
||||||
// Update SurfaceTexture dimensions
|
// Update SurfaceTexture dimensions
|
||||||
self.surface_size.width = width;
|
self.surface_size.width = width;
|
||||||
|
@ -338,6 +357,8 @@ impl Pixels {
|
||||||
self.context
|
self.context
|
||||||
.scaling_renderer
|
.scaling_renderer
|
||||||
.resize(&self.context.queue, width, height);
|
.resize(&self.context.queue, width, height);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw this pixel buffer to the configured [`SurfaceTexture`].
|
/// Draw this pixel buffer to the configured [`SurfaceTexture`].
|
||||||
|
|
Loading…
Reference in a new issue