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:
parent
480764ed5a
commit
e08c91bfd2
|
@ -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" }
|
||||||
|
|
13
README.md
13
README.md
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 = "../.." }
|
||||||
|
|
|
@ -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,
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 = "../.." }
|
||||||
|
|
|
@ -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(())
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
63
src/lib.rs
63
src/lib.rs
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
}],
|
}],
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue