Update to wgpu 0.10 (#187)

- It would be nice to return an error from the render function
- imgui-winit is still a WIP (open PR: https://github.com/Yatekii/imgui-wgpu-rs/pull/66)
- Update README

Co-authored-by: Mohammed Alyousef <mohammed.alyousef@neurosrg.com>
This commit is contained in:
Jay Oster 2021-09-01 14:50:43 -07:00 committed by GitHub
parent 480764ed5a
commit e08c91bfd2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 98 additions and 108 deletions

View file

@ -24,7 +24,7 @@ pollster = "0.2"
raw-window-handle = "0.3" raw-window-handle = "0.3"
thiserror = "1.0" thiserror = "1.0"
ultraviolet = "0.8" ultraviolet = "0.8"
wgpu = "0.9" wgpu = "0.10"
[dev-dependencies] [dev-dependencies]
pixels-mocks = { path = "internals/pixels-mocks" } pixels-mocks = { path = "internals/pixels-mocks" }

View file

@ -19,7 +19,8 @@ The Minimum Supported Rust Version for `pixels` will always be made available in
## Features ## Features
- Built on modern graphics APIs powered by [`wgpu`](https://crates.io/crates/wgpu): DirectX 12, Vulkan, Metal. OpenGL support is a work in progress. - Built on modern graphics APIs powered by [`wgpu`](https://crates.io/crates/wgpu): Vulkan, Metal, DirectX 12, OpenGL ES3.
- DirectX 11, WebGL2, and WebGPU support are a work in progress.
- Use your own custom shaders for special effects. - Use your own custom shaders for special effects.
- Hardware accelerated scaling on perfect pixel boundaries. - Hardware accelerated scaling on perfect pixel boundaries.
- Supports non-square pixel aspect ratios. (WIP) - Supports non-square pixel aspect ratios. (WIP)
@ -38,6 +39,16 @@ The Minimum Supported Rust Version for `pixels` will always be made available in
## Troubleshooting ## Troubleshooting
### Cargo resolver
Starting with [`wgpu` 0.10](https://github.com/gfx-rs/wgpu/blob/06316c1bac8b78ac04d762cfb1a886bd1d453b30/CHANGELOG.md#v010-2021-08-18), the [resolver version](https://doc.rust-lang.org/cargo/reference/resolver.html#resolver-versions) needs to be set in your `Cargo.toml` to avoid build errors:
```toml
resolver = "2"
```
### Driver issues
The most common issue is having an outdated graphics driver installed on the host machine. `pixels` The most common issue is having an outdated graphics driver installed on the host machine. `pixels`
requests a low power (aka integrated) GPU by default. If the examples are not working for any reason, you may try setting the `PIXELS_HIGH_PERF` environment variable (the value does not matter, e.g. `PIXELS_HIGH_PERF=1` is fine) to see if that addresses the issue on your host machine. requests a low power (aka integrated) GPU by default. If the examples are not working for any reason, you may try setting the `PIXELS_HIGH_PERF` environment variable (the value does not matter, e.g. `PIXELS_HIGH_PERF=1` is fine) to see if that addresses the issue on your host machine.

View file

@ -48,11 +48,11 @@ impl NoiseRenderer {
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("NoiseRenderer vertex buffer"), label: Some("NoiseRenderer vertex buffer"),
contents: vertex_data_slice, contents: vertex_data_slice,
usage: wgpu::BufferUsage::VERTEX, usage: wgpu::BufferUsages::VERTEX,
}); });
let vertex_buffer_layout = wgpu::VertexBufferLayout { let vertex_buffer_layout = wgpu::VertexBufferLayout {
array_stride: (vertex_data_slice.len() / vertex_data.len()) as wgpu::BufferAddress, array_stride: (vertex_data_slice.len() / vertex_data.len()) as wgpu::BufferAddress,
step_mode: wgpu::InputStepMode::Vertex, step_mode: wgpu::VertexStepMode::Vertex,
attributes: &[ attributes: &[
wgpu::VertexAttribute { wgpu::VertexAttribute {
format: wgpu::VertexFormat::Float32x2, format: wgpu::VertexFormat::Float32x2,
@ -71,7 +71,7 @@ impl NoiseRenderer {
let time_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { let time_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("NoiseRenderer u_Time"), label: Some("NoiseRenderer u_Time"),
contents: &0.0_f32.to_ne_bytes(), contents: &0.0_f32.to_ne_bytes(),
usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
}); });
// Create bind group // Create bind group
@ -80,7 +80,7 @@ impl NoiseRenderer {
entries: &[ entries: &[
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 0, binding: 0,
visibility: wgpu::ShaderStage::FRAGMENT, visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture { ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: true }, sample_type: wgpu::TextureSampleType::Float { filterable: true },
multisampled: false, multisampled: false,
@ -90,7 +90,7 @@ impl NoiseRenderer {
}, },
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 1, binding: 1,
visibility: wgpu::ShaderStage::FRAGMENT, visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Sampler { ty: wgpu::BindingType::Sampler {
filtering: true, filtering: true,
comparison: false, comparison: false,
@ -99,7 +99,7 @@ impl NoiseRenderer {
}, },
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 2, binding: 2,
visibility: wgpu::ShaderStage::FRAGMENT, visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Buffer { ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform, ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false, has_dynamic_offset: false,
@ -143,7 +143,7 @@ impl NoiseRenderer {
color: wgpu::BlendComponent::REPLACE, color: wgpu::BlendComponent::REPLACE,
alpha: wgpu::BlendComponent::REPLACE, alpha: wgpu::BlendComponent::REPLACE,
}), }),
write_mask: wgpu::ColorWrite::ALL, write_mask: wgpu::ColorWrites::ALL,
}], }],
}), }),
}); });
@ -217,7 +217,7 @@ fn create_texture_view(pixels: &pixels::Pixels, width: u32, height: u32) -> wgpu
sample_count: 1, sample_count: 1,
dimension: wgpu::TextureDimension::D2, dimension: wgpu::TextureDimension::D2,
format: pixels.render_texture_format(), format: pixels.render_texture_format(),
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::RENDER_ATTACHMENT, usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::RENDER_ATTACHMENT,
}; };
device device

View file

@ -10,9 +10,9 @@ optimize = ["log/release_max_level_warn"]
default = ["optimize"] default = ["optimize"]
[dependencies] [dependencies]
egui = "0.13" egui = "0.14"
egui_wgpu_backend = "0.10" egui_wgpu_backend = "0.12"
egui_winit_platform = { version = "0.9", features = ["webbrowser"] } egui_winit_platform = { version = "0.10", features = ["webbrowser"] }
env_logger = "0.9" env_logger = "0.9"
log = "0.4" log = "0.4"
pixels = { path = "../.." } pixels = { path = "../.." }

View file

@ -1,8 +1,9 @@
use egui::{ClippedMesh, FontDefinitions}; use egui::{ClippedMesh, FontDefinitions};
use egui_wgpu_backend::{RenderPass, ScreenDescriptor}; use egui_wgpu_backend::{BackendError, RenderPass, ScreenDescriptor};
use egui_winit_platform::{Platform, PlatformDescriptor}; use egui_winit_platform::{Platform, PlatformDescriptor};
use pixels::{wgpu, PixelsContext}; use pixels::{wgpu, PixelsContext};
use std::time::Instant; use std::time::Instant;
use winit::window::Window;
/// Manages all state required for rendering egui over `Pixels`. /// Manages all state required for rendering egui over `Pixels`.
pub(crate) struct Gui { pub(crate) struct Gui {
@ -63,7 +64,7 @@ impl Gui {
} }
/// Prepare egui. /// Prepare egui.
pub(crate) fn prepare(&mut self) { pub(crate) fn prepare(&mut self, window: &Window) {
self.platform self.platform
.update_time(self.start_time.elapsed().as_secs_f64()); .update_time(self.start_time.elapsed().as_secs_f64());
@ -74,7 +75,7 @@ impl Gui {
self.ui(&self.platform.context()); self.ui(&self.platform.context());
// End the egui frame and create all paint jobs to prepare for rendering. // End the egui frame and create all paint jobs to prepare for rendering.
let (_output, paint_commands) = self.platform.end_frame(); let (_output, paint_commands) = self.platform.end_frame(Some(window));
self.paint_jobs = self.platform.context().tessellate(paint_commands); self.paint_jobs = self.platform.context().tessellate(paint_commands);
} }
@ -112,7 +113,7 @@ impl Gui {
encoder: &mut wgpu::CommandEncoder, encoder: &mut wgpu::CommandEncoder,
render_target: &wgpu::TextureView, render_target: &wgpu::TextureView,
context: &PixelsContext, context: &PixelsContext,
) { ) -> Result<(), BackendError> {
// Upload all resources to the GPU. // Upload all resources to the GPU.
self.rpass.update_texture( self.rpass.update_texture(
&context.device, &context.device,
@ -135,6 +136,6 @@ impl Gui {
&self.paint_jobs, &self.paint_jobs,
&self.screen_descriptor, &self.screen_descriptor,
None, None,
); )
} }
} }

View file

@ -59,7 +59,7 @@ fn main() -> Result<(), Error> {
world.draw(pixels.get_frame()); world.draw(pixels.get_frame());
// Prepare egui // Prepare egui
gui.prepare(); gui.prepare(&window);
// Render everything together // Render everything together
let render_result = pixels.render_with(|encoder, render_target, context| { let render_result = pixels.render_with(|encoder, render_target, context| {
@ -67,7 +67,8 @@ fn main() -> Result<(), Error> {
context.scaling_renderer.render(encoder, render_target); context.scaling_renderer.render(encoder, render_target);
// Render egui // Render egui
gui.render(encoder, render_target, context); gui.render(encoder, render_target, context)
.expect("egui render error");
}); });
// Basic error handling // Basic error handling

View file

@ -12,7 +12,7 @@ default = ["optimize"]
[dependencies] [dependencies]
env_logger = "0.9" env_logger = "0.9"
imgui = "0.7" imgui = "0.7"
imgui-wgpu = "0.16" imgui-wgpu = { git = "https://github.com/kylc/imgui-wgpu-rs.git", rev = "fbbc0cfc7de25a4821980cb2170195c7b3b0fcf4" }
imgui-winit-support = { version = "0.7.1", default-features = false, features = ["winit-25"] } imgui-winit-support = { version = "0.7.1", default-features = false, features = ["winit-25"] }
log = "0.4" log = "0.4"
pixels = { path = "../.." } pixels = { path = "../.." }

View file

@ -5,12 +5,10 @@
use fltk::{app, enums::Event, prelude::*, window::Window}; use fltk::{app, enums::Event, prelude::*, window::Window};
use log::error; use log::error;
use pixels::{Error, Pixels, SurfaceTexture}; use pixels::{Error, Pixels, SurfaceTexture};
use std::{thread, time::Duration};
const WIDTH: u32 = 600; const WIDTH: u32 = 600;
const HEIGHT: u32 = 400; const HEIGHT: u32 = 400;
const CIRCLE_RADIUS: i16 = 64; const CIRCLE_RADIUS: i16 = 64;
const SLEEP: u64 = 16;
/// Representation of the application state. In this example, a circle will bounce around the screen. /// Representation of the application state. In this example, a circle will bounce around the screen.
struct World { struct World {
@ -59,10 +57,9 @@ fn main() -> Result<(), Error> {
{ {
app.quit(); app.quit();
} }
win.redraw();
// Calls to redraw in the event loop require an explicit sleep app::flush();
thread::sleep(Duration::from_millis(SLEEP)); app::awake();
} }
Ok(()) Ok(())

View file

@ -8,7 +8,7 @@ use std::env;
pub struct PixelsBuilder<'req, 'dev, 'win, W: HasRawWindowHandle> { pub struct PixelsBuilder<'req, 'dev, 'win, W: HasRawWindowHandle> {
request_adapter_options: Option<wgpu::RequestAdapterOptions<'req>>, request_adapter_options: Option<wgpu::RequestAdapterOptions<'req>>,
device_descriptor: wgpu::DeviceDescriptor<'dev>, device_descriptor: wgpu::DeviceDescriptor<'dev>,
backend: wgpu::BackendBit, backend: wgpu::Backends,
width: u32, width: u32,
height: u32, height: u32,
_pixel_aspect_ratio: f64, _pixel_aspect_ratio: f64,
@ -51,7 +51,7 @@ impl<'req, 'dev, 'win, W: HasRawWindowHandle> PixelsBuilder<'req, 'dev, 'win, W>
PixelsBuilder { PixelsBuilder {
request_adapter_options: None, request_adapter_options: None,
device_descriptor: wgpu::DeviceDescriptor::default(), device_descriptor: wgpu::DeviceDescriptor::default(),
backend: wgpu::BackendBit::PRIMARY, backend: wgpu::Backends::PRIMARY,
width, width,
height, height,
_pixel_aspect_ratio: 1.0, _pixel_aspect_ratio: 1.0,
@ -83,7 +83,7 @@ impl<'req, 'dev, 'win, W: HasRawWindowHandle> PixelsBuilder<'req, 'dev, 'win, W>
/// Set which backends wgpu will attempt to use. /// Set which backends wgpu will attempt to use.
/// ///
/// The default value is `PRIMARY`, which enables the well supported backends for wgpu. /// The default value is `PRIMARY`, which enables the well supported backends for wgpu.
pub fn wgpu_backend(mut self, backend: wgpu::BackendBit) -> PixelsBuilder<'req, 'dev, 'win, W> { pub fn wgpu_backend(mut self, backend: wgpu::Backends) -> PixelsBuilder<'req, 'dev, 'win, W> {
self.backend = backend; self.backend = backend;
self self
} }
@ -159,19 +159,19 @@ impl<'req, 'dev, 'win, W: HasRawWindowHandle> PixelsBuilder<'req, 'dev, 'win, W>
/// Set the render texture format. /// Set the render texture format.
/// ///
/// The default value is chosen automatically by the swapchain (if it can) with a fallback to /// The default value is chosen automatically by the surface (if it can) with a fallback to
/// `Bgra8UnormSrgb` (which is 4 unsigned bytes in `BGRA` order using the sRGB color space). /// `Bgra8UnormSrgb` (which is 4 unsigned bytes in `BGRA` order using the sRGB color space).
/// Setting this format correctly depends on the hardware/platform the pixel buffer is rendered /// Setting this format correctly depends on the hardware/platform the pixel buffer is rendered
/// to. The chosen format can be retrieved later with [`Pixels::render_texture_format`]. /// to. The chosen format can be retrieved later with [`Pixels::render_texture_format`].
/// ///
/// This method controls the format of the swapchain frame buffer, which has strict texture /// This method controls the format of the surface frame buffer, which has strict texture
/// format requirements. Applications will never interact directly with the pixel data of this /// format requirements. Applications will never interact directly with the pixel data of this
/// texture, but a view is provided to the `render_function` closure by [`Pixels::render_with`]. /// texture, but a view is provided to the `render_function` closure by [`Pixels::render_with`].
/// The render texture can only be used as the final render target at the end of all /// The render texture can only be used as the final render target at the end of all
/// post-processing shaders. /// post-processing shaders.
/// ///
/// The [`ScalingRenderer`] also uses this format for its own render target. This is because it /// The [`ScalingRenderer`] also uses this format for its own render target. This is because it
/// assumes the render target is always the swapchain current frame. This needs to be kept in /// assumes the render target is always the surface current frame. This needs to be kept in
/// mind when writing custom shaders for post-processing effects. There is a full example of a /// mind when writing custom shaders for post-processing effects. There is a full example of a
/// [custom-shader](https://github.com/parasyte/pixels/tree/master/examples/custom-shader) /// [custom-shader](https://github.com/parasyte/pixels/tree/master/examples/custom-shader)
/// available that demonstrates how to deal with this. /// available that demonstrates how to deal with this.
@ -206,28 +206,19 @@ impl<'req, 'dev, 'win, W: HasRawWindowHandle> PixelsBuilder<'req, 'dev, 'win, W>
)); ));
let adapter = pollster::block_on(adapter).ok_or(Error::AdapterNotFound)?; let adapter = pollster::block_on(adapter).ok_or(Error::AdapterNotFound)?;
let (mut device, queue) = let (device, queue) =
pollster::block_on(adapter.request_device(&self.device_descriptor, None)) pollster::block_on(adapter.request_device(&self.device_descriptor, None))
.map_err(Error::DeviceNotFound)?; .map_err(Error::DeviceNotFound)?;
let present_mode = self.present_mode; let present_mode = self.present_mode;
let render_texture_format = self.render_texture_format.unwrap_or_else(|| { let render_texture_format = self.render_texture_format.unwrap_or_else(|| {
adapter surface
.get_swap_chain_preferred_format(&surface) .get_preferred_format(&adapter)
.unwrap_or(wgpu::TextureFormat::Bgra8UnormSrgb) .unwrap_or(wgpu::TextureFormat::Bgra8UnormSrgb)
}); });
// Create swap chain
let surface_size = self.surface_texture.size;
let swap_chain = create_swap_chain(
&mut device,
&surface,
render_texture_format,
&surface_size,
present_mode,
);
// Create the backing texture // Create the backing texture
let surface_size = self.surface_texture.size;
let (scaling_matrix_inverse, texture_extent, texture, scaling_renderer, pixels_buffer_size) = let (scaling_matrix_inverse, texture_extent, texture, scaling_renderer, pixels_buffer_size) =
create_backing_texture( create_backing_texture(
&device, &device,
@ -249,7 +240,6 @@ impl<'req, 'dev, 'win, W: HasRawWindowHandle> PixelsBuilder<'req, 'dev, 'win, W>
device, device,
queue, queue,
surface, surface,
swap_chain,
texture, texture,
texture_extent, texture_extent,
texture_format: self.texture_format, texture_format: self.texture_format,
@ -257,34 +247,18 @@ impl<'req, 'dev, 'win, W: HasRawWindowHandle> PixelsBuilder<'req, 'dev, 'win, W>
scaling_renderer, scaling_renderer,
}; };
Ok(Pixels { let pixels = Pixels {
context, context,
surface_size, surface_size,
present_mode, present_mode,
render_texture_format, render_texture_format,
pixels, pixels,
scaling_matrix_inverse, scaling_matrix_inverse,
}) };
} pixels.reconfigure_surface();
}
pub(crate) fn create_swap_chain( Ok(pixels)
device: &mut wgpu::Device, }
surface: &wgpu::Surface,
format: wgpu::TextureFormat,
surface_size: &SurfaceSize,
present_mode: wgpu::PresentMode,
) -> wgpu::SwapChain {
device.create_swap_chain(
surface,
&wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::RENDER_ATTACHMENT,
format,
width: surface_size.width,
height: surface_size.height,
present_mode,
},
)
} }
pub(crate) fn create_backing_texture( pub(crate) fn create_backing_texture(
@ -321,7 +295,7 @@ pub(crate) fn create_backing_texture(
sample_count: 1, sample_count: 1,
dimension: wgpu::TextureDimension::D2, dimension: wgpu::TextureDimension::D2,
format: backing_texture_format, format: backing_texture_format,
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST, usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
}); });
let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default()); let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default());
@ -366,7 +340,8 @@ const fn get_texture_format_size(texture_format: wgpu::TextureFormat) -> f32 {
| Rg8Unorm | Rg8Unorm
| Rg8Snorm | Rg8Snorm
| Rg8Uint | Rg8Uint
| Rg8Sint => 2.0, // 16.0 / 8.0 | Rg8Sint
| Rgb9e5Ufloat => 2.0, // 16.0 / 8.0
// 32-bit formats, 8 bits per component // 32-bit formats, 8 bits per component
R32Uint R32Uint
@ -426,10 +401,8 @@ const fn get_texture_format_size(texture_format: wgpu::TextureFormat) -> f32 {
| Bc6hRgbSfloat | Bc6hRgbSfloat
| Bc7RgbaUnorm | Bc7RgbaUnorm
| Bc7RgbaUnormSrgb | Bc7RgbaUnormSrgb
| Etc2RgbA8Unorm | EacRgUnorm
| Etc2RgbA8UnormSrgb | EacRgSnorm
| EtcRgUnorm
| EtcRgSnorm
| Astc4x4RgbaUnorm | Astc4x4RgbaUnorm
| Astc4x4RgbaUnormSrgb => 1.0, // 4.0 * 4.0 / 16.0 | Astc4x4RgbaUnormSrgb => 1.0, // 4.0 * 4.0 / 16.0

View file

@ -31,11 +31,10 @@
pub use crate::builder::PixelsBuilder; pub use crate::builder::PixelsBuilder;
pub use crate::renderers::ScalingRenderer; pub use crate::renderers::ScalingRenderer;
pub use raw_window_handle; pub use raw_window_handle;
use std::num::NonZeroU32;
pub use wgpu;
use raw_window_handle::HasRawWindowHandle; use raw_window_handle::HasRawWindowHandle;
use std::num::NonZeroU32;
use thiserror::Error; use thiserror::Error;
pub use wgpu;
mod builder; mod builder;
mod renderers; mod renderers;
@ -67,7 +66,6 @@ pub struct PixelsContext {
pub queue: wgpu::Queue, pub queue: wgpu::Queue,
surface: wgpu::Surface, surface: wgpu::Surface,
swap_chain: wgpu::SwapChain,
/// This is the texture that your raw data is copied to by [`Pixels::render`] or /// This is the texture that your raw data is copied to by [`Pixels::render`] or
/// [`Pixels::render_with`]. /// [`Pixels::render_with`].
@ -114,9 +112,9 @@ pub enum Error {
/// Equivalent to [`wgpu::RequestDeviceError`] /// Equivalent to [`wgpu::RequestDeviceError`]
#[error("No wgpu::Device found.")] #[error("No wgpu::Device found.")]
DeviceNotFound(wgpu::RequestDeviceError), DeviceNotFound(wgpu::RequestDeviceError),
/// Equivalent to [`wgpu::SwapChainError`] /// Equivalent to [`wgpu::SurfaceError`]
#[error("The GPU failed to acquire a swapchain frame.")] #[error("The GPU failed to acquire a surface frame.")]
Swapchain(wgpu::SwapChainError), Surface(wgpu::SurfaceError),
} }
impl<'win, W: HasRawWindowHandle> SurfaceTexture<'win, W> { impl<'win, W: HasRawWindowHandle> SurfaceTexture<'win, W> {
@ -265,8 +263,8 @@ impl Pixels {
.transform .transform
.inversed(); .inversed();
// Recreate the swap chain // Reconfigure the surface
self.recreate_swap_chain(); self.reconfigure_surface();
// Update state for all render passes // Update state for all render passes
self.context self.context
@ -278,7 +276,7 @@ impl Pixels {
/// ///
/// # Errors /// # Errors
/// ///
/// Returns an error when [`wgpu::SwapChain::get_current_frame`] fails. /// Returns an error when [`wgpu::Surface::get_current_frame`] fails.
/// ///
/// # Example /// # Example
/// ///
@ -310,13 +308,13 @@ impl Pixels {
/// Draw this pixel buffer to the configured [`SurfaceTexture`] using a custom user-provided /// Draw this pixel buffer to the configured [`SurfaceTexture`] using a custom user-provided
/// render function. /// render function.
/// ///
/// Provides access to a [`wgpu::CommandEncoder`], a [`wgpu::TextureView`] from the swapchain /// Provides access to a [`wgpu::CommandEncoder`], a [`wgpu::TextureView`] from the surface
/// which you can use to render to the screen, and a [`PixelsContext`] with all of the internal /// which you can use to render to the screen, and a [`PixelsContext`] with all of the internal
/// `wgpu` context. /// `wgpu` context.
/// ///
/// # Errors /// # Errors
/// ///
/// Returns an error when [`wgpu::SwapChain::get_current_frame`] fails. /// Returns an error when [`wgpu::Surface::get_current_frame`] fails.
/// ///
/// # Example /// # Example
/// ///
@ -348,18 +346,18 @@ impl Pixels {
{ {
let frame = self let frame = self
.context .context
.swap_chain .surface
.get_current_frame() .get_current_frame()
.or_else(|err| match err { .or_else(|err| match err {
wgpu::SwapChainError::Outdated => { wgpu::SurfaceError::Outdated => {
// Recreate the swap chain to mitigate race condition on drawing surface resize. // Reconfigure the surface to mitigate race condition on window resize.
// See https://github.com/parasyte/pixels/issues/121 // See https://github.com/parasyte/pixels/issues/121
self.recreate_swap_chain(); self.reconfigure_surface();
self.context.swap_chain.get_current_frame() self.context.surface.get_current_frame()
} }
err => Err(err), err => Err(err),
}) })
.map_err(Error::Swapchain)?; .map_err(Error::Surface)?;
let mut encoder = let mut encoder =
self.context self.context
.device .device
@ -375,6 +373,7 @@ impl Pixels {
texture: &self.context.texture, texture: &self.context.texture,
mip_level: 0, mip_level: 0,
origin: wgpu::Origin3d { x: 0, y: 0, z: 0 }, origin: wgpu::Origin3d { x: 0, y: 0, z: 0 },
aspect: wgpu::TextureAspect::All,
}, },
&self.pixels, &self.pixels,
wgpu::ImageDataLayout { wgpu::ImageDataLayout {
@ -385,23 +384,31 @@ impl Pixels {
self.context.texture_extent, self.context.texture_extent,
); );
let view = frame
.output
.texture
.create_view(&wgpu::TextureViewDescriptor::default());
// Call the users render function. // Call the users render function.
(render_function)(&mut encoder, &frame.output.view, &self.context); (render_function)(&mut encoder, &view, &self.context);
self.context.queue.submit(Some(encoder.finish())); self.context.queue.submit(Some(encoder.finish()));
Ok(()) Ok(())
} }
/// Recreate the swap chain. /// Reconfigure the surface.
/// ///
/// Call this when the surface or presentation mode needs to be changed. /// Call this when the surface or presentation mode needs to be changed.
pub(crate) fn recreate_swap_chain(&mut self) { pub(crate) fn reconfigure_surface(&self) {
self.context.swap_chain = builder::create_swap_chain( self.context.surface.configure(
&mut self.context.device, &self.context.device,
&self.context.surface, &wgpu::SurfaceConfiguration {
self.render_texture_format, usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
&self.surface_size, format: self.render_texture_format,
self.present_mode, width: self.surface_size.width,
height: self.surface_size.height,
present_mode: self.present_mode,
},
); );
} }
@ -532,7 +539,7 @@ impl Pixels {
/// Get the render texture format. /// Get the render texture format.
/// ///
/// This texture format may be chosen automatically by the swapchain. See /// This texture format may be chosen automatically by the surface. See
/// [`PixelsBuilder::render_texture_format`] for more information. /// [`PixelsBuilder::render_texture_format`] for more information.
pub fn render_texture_format(&self) -> wgpu::TextureFormat { pub fn render_texture_format(&self) -> wgpu::TextureFormat {
self.render_texture_format self.render_texture_format

View file

@ -54,11 +54,11 @@ impl ScalingRenderer {
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("pixels_scaling_renderer_vertex_buffer"), label: Some("pixels_scaling_renderer_vertex_buffer"),
contents: vertex_data_slice, contents: vertex_data_slice,
usage: wgpu::BufferUsage::VERTEX, usage: wgpu::BufferUsages::VERTEX,
}); });
let vertex_buffer_layout = wgpu::VertexBufferLayout { let vertex_buffer_layout = wgpu::VertexBufferLayout {
array_stride: (vertex_data_slice.len() / vertex_data.len()) as wgpu::BufferAddress, array_stride: (vertex_data_slice.len() / vertex_data.len()) as wgpu::BufferAddress,
step_mode: wgpu::InputStepMode::Vertex, step_mode: wgpu::VertexStepMode::Vertex,
attributes: &[ attributes: &[
wgpu::VertexAttribute { wgpu::VertexAttribute {
format: wgpu::VertexFormat::Float32x2, format: wgpu::VertexFormat::Float32x2,
@ -82,7 +82,7 @@ impl ScalingRenderer {
let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("pixels_scaling_renderer_matrix_uniform_buffer"), label: Some("pixels_scaling_renderer_matrix_uniform_buffer"),
contents: transform_bytes, contents: transform_bytes,
usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
}); });
// Create bind group // Create bind group
@ -91,7 +91,7 @@ impl ScalingRenderer {
entries: &[ entries: &[
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 0, binding: 0,
visibility: wgpu::ShaderStage::FRAGMENT, visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture { ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: true }, sample_type: wgpu::TextureSampleType::Float { filterable: true },
multisampled: false, multisampled: false,
@ -101,7 +101,7 @@ impl ScalingRenderer {
}, },
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 1, binding: 1,
visibility: wgpu::ShaderStage::FRAGMENT, visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Sampler { ty: wgpu::BindingType::Sampler {
filtering: true, filtering: true,
comparison: false, comparison: false,
@ -110,7 +110,7 @@ impl ScalingRenderer {
}, },
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 2, binding: 2,
visibility: wgpu::ShaderStage::VERTEX, visibility: wgpu::ShaderStages::VERTEX,
ty: wgpu::BindingType::Buffer { ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform, ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false, has_dynamic_offset: false,
@ -169,7 +169,7 @@ impl ScalingRenderer {
color: wgpu::BlendComponent::REPLACE, color: wgpu::BlendComponent::REPLACE,
alpha: wgpu::BlendComponent::REPLACE, alpha: wgpu::BlendComponent::REPLACE,
}), }),
write_mask: wgpu::ColorWrite::ALL, write_mask: wgpu::ColorWrites::ALL,
}], }],
}), }),
}); });