From 00a8e9c92c7f2725e289bf5fa253069ab88f7986 Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Sat, 5 Feb 2022 18:38:45 -0500 Subject: [PATCH 01/11] Make BlendState configurable --- src/builder.rs | 35 ++++++++++++++++++++++++++++++++++- src/lib.rs | 2 ++ src/renderers.rs | 3 ++- 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index 32932261..721fe4ed 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -17,6 +17,7 @@ pub struct PixelsBuilder<'req, 'dev, 'win, W: HasRawWindowHandle> { render_texture_format: Option, surface_texture_format: Option, clear_color: wgpu::Color, + blend_state: wgpu::BlendState, } impl<'req, 'dev, 'win, W: HasRawWindowHandle> PixelsBuilder<'req, 'dev, 'win, W> { @@ -71,6 +72,7 @@ impl<'req, 'dev, 'win, W: HasRawWindowHandle> PixelsBuilder<'req, 'dev, 'win, W> render_texture_format: None, surface_texture_format: None, clear_color: wgpu::Color::BLACK, + blend_state: wgpu::BlendState::ALPHA_BLENDING, } } @@ -189,11 +191,37 @@ impl<'req, 'dev, 'win, W: HasRawWindowHandle> PixelsBuilder<'req, 'dev, 'win, W> self } - /// Set the default clear color. + /// Set the blend state. + /// + /// Allows customization of how to mix the new and new existing pixels in a texture + /// when rendering. + /// + /// The default blend state is alpha blending with non-premultiplied alpha. + /// + /// ```no_run + /// use pixels::wgpu::BlendState; + /// + /// # use pixels::PixelsBuilder; + /// # let window = pixels_mocks::Rwh; + /// # let surface_texture = pixels::SurfaceTexture::new(320, 240, &window); + /// // Replace the old pixels with the new without mixing. + /// let mut pixels = PixelsBuilder::new(320, 240, surface_texture) + /// .blend_state(wgpu::BlendState::REPLACE) + /// .build()?; + /// # Ok::<(), pixels::Error>(()) + /// ``` + pub fn blend_state(mut self, blend_state: wgpu::BlendState) -> Self { + self.blend_state = blend_state; + self + } + + /// Set the clear color. /// /// Allows customization of the background color and the border drawn for non-integer scale /// values. /// + /// The default value is pure black. + /// /// ```no_run /// use pixels::wgpu::Color; /// @@ -277,6 +305,7 @@ impl<'req, 'dev, 'win, W: HasRawWindowHandle> PixelsBuilder<'req, 'dev, 'win, W> // Create the backing texture let surface_size = self.surface_texture.size; let clear_color = self.clear_color; + let blend_state = self.blend_state; let (scaling_matrix_inverse, texture_extent, texture, scaling_renderer, pixels_buffer_size) = create_backing_texture( &device, @@ -288,6 +317,7 @@ impl<'req, 'dev, 'win, W: HasRawWindowHandle> PixelsBuilder<'req, 'dev, 'win, W> &surface_size, render_texture_format, clear_color, + blend_state, ); // Create the pixel buffer @@ -312,6 +342,7 @@ impl<'req, 'dev, 'win, W: HasRawWindowHandle> PixelsBuilder<'req, 'dev, 'win, W> present_mode, render_texture_format, surface_texture_format, + blend_state, pixels, scaling_matrix_inverse, }; @@ -368,6 +399,7 @@ pub(crate) fn create_backing_texture( surface_size: &SurfaceSize, render_texture_format: wgpu::TextureFormat, clear_color: wgpu::Color, + blend_state: wgpu::BlendState, ) -> ( ultraviolet::Mat4, wgpu::Extent3d, @@ -406,6 +438,7 @@ pub(crate) fn create_backing_texture( surface_size, render_texture_format, clear_color, + blend_state, ); let texture_format_size = get_texture_format_size(backing_texture_format); diff --git a/src/lib.rs b/src/lib.rs index 926b3b2a..0b436507 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -98,6 +98,7 @@ pub struct Pixels { present_mode: wgpu::PresentMode, render_texture_format: wgpu::TextureFormat, surface_texture_format: wgpu::TextureFormat, + blend_state: wgpu::BlendState, // Pixel buffer pixels: Vec, @@ -285,6 +286,7 @@ impl Pixels { &self.surface_size, self.render_texture_format, self.context.scaling_renderer.clear_color, + self.blend_state, ); self.scaling_matrix_inverse = scaling_matrix_inverse; diff --git a/src/renderers.rs b/src/renderers.rs index 44cd70cc..e148c9b7 100644 --- a/src/renderers.rs +++ b/src/renderers.rs @@ -23,6 +23,7 @@ impl ScalingRenderer { surface_size: &SurfaceSize, render_texture_format: wgpu::TextureFormat, clear_color: wgpu::Color, + blend_state: wgpu::BlendState, ) -> Self { let shader = wgpu::include_wgsl!("../shaders/scale.wgsl"); let module = device.create_shader_module(&shader); @@ -152,7 +153,7 @@ impl ScalingRenderer { entry_point: "fs_main", targets: &[wgpu::ColorTargetState { format: render_texture_format, - blend: Some(wgpu::BlendState::ALPHA_BLENDING), + blend: Some(blend_state), write_mask: wgpu::ColorWrites::ALL, }], }), From d16d976b274a0d57c6aacceb7c99b03f6263d5e9 Mon Sep 17 00:00:00 2001 From: Jay Oster Date: Sat, 5 Feb 2022 23:23:02 -0800 Subject: [PATCH 02/11] Fix typo --- src/builder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/builder.rs b/src/builder.rs index 721fe4ed..e7c9525b 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -193,7 +193,7 @@ impl<'req, 'dev, 'win, W: HasRawWindowHandle> PixelsBuilder<'req, 'dev, 'win, W> /// Set the blend state. /// - /// Allows customization of how to mix the new and new existing pixels in a texture + /// Allows customization of how to mix the new and existing pixels in a texture /// when rendering. /// /// The default blend state is alpha blending with non-premultiplied alpha. From 5820cfb6c333d962430cb36b95524743696776ea Mon Sep 17 00:00:00 2001 From: Jay Oster Date: Sun, 6 Feb 2022 01:45:34 -0800 Subject: [PATCH 03/11] Clippy --- src/builder.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/builder.rs b/src/builder.rs index e7c9525b..40d7c4a2 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -391,6 +391,7 @@ impl<'req, 'dev, 'win, W: HasRawWindowHandle> PixelsBuilder<'req, 'dev, 'win, W> } } +#[allow(clippy::too_many_arguments)] pub(crate) fn create_backing_texture( device: &wgpu::Device, width: u32, From 2c24edef2d6d2d261a94464faa0854c805cb58d5 Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Sun, 17 Jul 2022 17:05:41 -0700 Subject: [PATCH 04/11] Intial work towards wgpu 0.13 --- Cargo.toml | 6 +- examples/conway/Cargo.toml | 4 +- examples/custom-shader/Cargo.toml | 4 +- examples/custom-shader/shaders/noise.wgsl | 24 +++--- examples/custom-shader/src/renderers.rs | 10 +-- examples/imgui-winit/Cargo.toml | 4 +- examples/imgui-winit/src/gui.rs | 4 +- examples/invaders/Cargo.toml | 6 +- examples/minimal-egui/Cargo.toml | 8 +- examples/minimal-fltk/Cargo.toml | 2 +- examples/minimal-web/Cargo.toml | 4 +- examples/minimal-winit/Cargo.toml | 2 +- examples/raqote-winit/Cargo.toml | 2 +- shaders/scale.wgsl | 24 +++--- src/builder.rs | 93 +++++------------------ src/renderers.rs | 10 +-- 16 files changed, 78 insertions(+), 129 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8a2efdc3..3c801df0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,11 +19,11 @@ include = [ ] [dependencies] -bytemuck = "1.7" +bytemuck = "1.10" raw-window-handle = "0.4" thiserror = "1.0" -ultraviolet = "0.8" -wgpu = "0.12" +ultraviolet = "0.9" +wgpu = "0.13" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] pollster = "0.2" diff --git a/examples/conway/Cargo.toml b/examples/conway/Cargo.toml index b348c08c..548917b2 100644 --- a/examples/conway/Cargo.toml +++ b/examples/conway/Cargo.toml @@ -10,7 +10,7 @@ optimize = ["log/release_max_level_warn"] default = ["optimize"] [dependencies] -byteorder = "1.3" +byteorder = "1.4" env_logger = "0.9" getrandom = "0.2" line_drawing = "1.0" @@ -18,4 +18,4 @@ log = "0.4" pixels = { path = "../.." } randomize = "3.0" winit = "0.26" -winit_input_helper = "0.11" +winit_input_helper = "0.12" diff --git a/examples/custom-shader/Cargo.toml b/examples/custom-shader/Cargo.toml index 880b5444..44b5baed 100644 --- a/examples/custom-shader/Cargo.toml +++ b/examples/custom-shader/Cargo.toml @@ -10,9 +10,9 @@ optimize = ["log/release_max_level_warn"] default = ["optimize"] [dependencies] -bytemuck = "1.7" +bytemuck = "1.10" env_logger = "0.9" log = "0.4" pixels = { path = "../.." } winit = "0.26" -winit_input_helper = "0.11" +winit_input_helper = "0.12" diff --git a/examples/custom-shader/shaders/noise.wgsl b/examples/custom-shader/shaders/noise.wgsl index 8f6c0014..fd7c5219 100644 --- a/examples/custom-shader/shaders/noise.wgsl +++ b/examples/custom-shader/shaders/noise.wgsl @@ -1,13 +1,13 @@ // Vertex shader bindings struct VertexOutput { - [[location(0)]] tex_coord: vec2; - [[builtin(position)]] position: vec4; -}; + @location(0) tex_coord: vec2, + @builtin(position) position: vec4, +} -[[stage(vertex)]] +@vertex fn vs_main( - [[location(0)]] position: vec2, + @location(0) position: vec2, ) -> VertexOutput { var out: VertexOutput; out.tex_coord = fma(position, vec2(0.5, -0.5), vec2(0.5, 0.5)); @@ -17,12 +17,12 @@ fn vs_main( // Fragment shader bindings -[[group(0), binding(0)]] var r_tex_color: texture_2d; -[[group(0), binding(1)]] var r_tex_sampler: sampler; +@group(0) @binding(0) var r_tex_color: texture_2d; +@group(0) @binding(1) var r_tex_sampler: sampler; struct Locals { - time: f32; -}; -[[group(0), binding(2)]] var r_locals: Locals; + time: f32, +} +@group(0) @binding(2) var r_locals: Locals; let tau: f32 = 6.283185307179586476925286766559; let bias: f32 = 0.2376; // Offset the circular time input so it is never 0 @@ -40,8 +40,8 @@ fn random_vec2(st: vec2) -> f32 { return random(dot(st, vec2(random_x, random_y))); } -[[stage(fragment)]] -fn fs_main([[location(0)]] tex_coord: vec2) -> [[location(0)]] vec4 { +@fragment +fn fs_main(@location(0) tex_coord: vec2) -> @location(0) vec4 { let sampled_color = textureSample(r_tex_color, r_tex_sampler, tex_coord); let noise_color = vec3(random_vec2(tex_coord.xy * vec2(r_locals.time % tau + bias))); diff --git a/examples/custom-shader/src/renderers.rs b/examples/custom-shader/src/renderers.rs index f5e4806d..22121465 100644 --- a/examples/custom-shader/src/renderers.rs +++ b/examples/custom-shader/src/renderers.rs @@ -14,7 +14,7 @@ impl NoiseRenderer { pub(crate) fn new(pixels: &pixels::Pixels, width: u32, height: u32) -> Self { let device = pixels.device(); 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 // This will be used as the render target for the default scaling renderer @@ -127,14 +127,14 @@ impl NoiseRenderer { fragment: Some(wgpu::FragmentState { module: &module, entry_point: "fs_main", - targets: &[wgpu::ColorTargetState { + targets: &[Some(wgpu::ColorTargetState { format: pixels.render_texture_format(), blend: Some(wgpu::BlendState { color: wgpu::BlendComponent::REPLACE, alpha: wgpu::BlendComponent::REPLACE, }), write_mask: wgpu::ColorWrites::ALL, - }], + })], }), multiview: None, }); @@ -177,14 +177,14 @@ impl NoiseRenderer { ) { let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: Some("NoiseRenderer render pass"), - color_attachments: &[wgpu::RenderPassColorAttachment { + color_attachments: &[Some(wgpu::RenderPassColorAttachment { view: render_target, resolve_target: None, ops: wgpu::Operations { load: wgpu::LoadOp::Clear(wgpu::Color::BLACK), store: true, }, - }], + })], depth_stencil_attachment: None, }); rpass.set_pipeline(&self.render_pipeline); diff --git a/examples/imgui-winit/Cargo.toml b/examples/imgui-winit/Cargo.toml index 56945c90..b858ac59 100644 --- a/examples/imgui-winit/Cargo.toml +++ b/examples/imgui-winit/Cargo.toml @@ -12,9 +12,9 @@ default = ["optimize"] [dependencies] env_logger = "0.9" imgui = "0.8" -imgui-wgpu = "0.19" +imgui-wgpu = "0.20" imgui-winit-support = { version = "0.8", default-features = false, features = ["winit-26"] } log = "0.4" pixels = { path = "../.." } winit = "0.26" -winit_input_helper = "0.11" +winit_input_helper = "0.12" diff --git a/examples/imgui-winit/src/gui.rs b/examples/imgui-winit/src/gui.rs index 90aba9ea..0b854ef2 100644 --- a/examples/imgui-winit/src/gui.rs +++ b/examples/imgui-winit/src/gui.rs @@ -108,14 +108,14 @@ impl Gui { // Render Dear ImGui with WGPU let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: Some("imgui"), - color_attachments: &[wgpu::RenderPassColorAttachment { + color_attachments: &[Some(wgpu::RenderPassColorAttachment { view: render_target, resolve_target: None, ops: wgpu::Operations { load: wgpu::LoadOp::Load, store: true, }, - }], + })], depth_stencil_attachment: None, }); diff --git a/examples/invaders/Cargo.toml b/examples/invaders/Cargo.toml index 8b65c192..63a78ee4 100644 --- a/examples/invaders/Cargo.toml +++ b/examples/invaders/Cargo.toml @@ -12,12 +12,12 @@ default = ["optimize"] [dependencies] byteorder = "1.3" env_logger = "0.9" -game-loop = { version = "0.8", features = ["window"] } +game-loop = { version = "0.9", features = ["window"] } getrandom = "0.2" -gilrs = "0.8" +gilrs = "0.9" log = "0.4" pixels = { path = "../.." } randomize = "3.0" simple-invaders = { path = "simple-invaders" } winit = "0.26" -winit_input_helper = "0.11" +winit_input_helper = "0.12" diff --git a/examples/minimal-egui/Cargo.toml b/examples/minimal-egui/Cargo.toml index 1268c6fa..4b804851 100644 --- a/examples/minimal-egui/Cargo.toml +++ b/examples/minimal-egui/Cargo.toml @@ -10,11 +10,11 @@ optimize = ["log/release_max_level_warn"] default = ["optimize"] [dependencies] -egui = "0.16" -egui_wgpu_backend = "0.16" -egui-winit = { version = "0.16", default-features = false, features = ["links"] } +egui = "0.18" +egui_wgpu_backend = "0.18" +egui-winit = { version = "0.18", default-features = false, features = ["links"] } env_logger = "0.9" log = "0.4" pixels = { path = "../.." } winit = "0.26" -winit_input_helper = "0.11" +winit_input_helper = "0.12" diff --git a/examples/minimal-fltk/Cargo.toml b/examples/minimal-fltk/Cargo.toml index ecef9b5c..74feb937 100644 --- a/examples/minimal-fltk/Cargo.toml +++ b/examples/minimal-fltk/Cargo.toml @@ -10,7 +10,7 @@ optimize = ["log/release_max_level_warn"] default = ["optimize"] [dependencies] -fltk = { version = "1.2", features = ["raw-window-handle", "no-images", "no-pango"] } +fltk = { version = "1.3", features = ["raw-window-handle", "no-images", "no-pango"] } env_logger = "0.9" log = "0.4" pixels = { path = "../.." } diff --git a/examples/minimal-web/Cargo.toml b/examples/minimal-web/Cargo.toml index 18945784..f45ed2e2 100644 --- a/examples/minimal-web/Cargo.toml +++ b/examples/minimal-web/Cargo.toml @@ -13,9 +13,9 @@ default = ["optimize"] [dependencies] log = "0.4" pixels = { path = "../.." } -wgpu = "0.12" +wgpu = "0.13" winit = "0.26" -winit_input_helper = "0.11" +winit_input_helper = "0.12" [target.'cfg(target_arch = "wasm32")'.dependencies] console_error_panic_hook = "0.1" diff --git a/examples/minimal-winit/Cargo.toml b/examples/minimal-winit/Cargo.toml index 8d2b6ce1..a8692883 100644 --- a/examples/minimal-winit/Cargo.toml +++ b/examples/minimal-winit/Cargo.toml @@ -14,4 +14,4 @@ env_logger = "0.9" log = "0.4" pixels = { path = "../.." } winit = "0.26" -winit_input_helper = "0.11" +winit_input_helper = "0.12" diff --git a/examples/raqote-winit/Cargo.toml b/examples/raqote-winit/Cargo.toml index f1b66f74..c559afcb 100644 --- a/examples/raqote-winit/Cargo.toml +++ b/examples/raqote-winit/Cargo.toml @@ -15,7 +15,7 @@ euclid = "0.22" log = "0.4" pixels = { path = "../.." } winit = "0.26" -winit_input_helper = "0.11" +winit_input_helper = "0.12" [dependencies.raqote] git = "https://github.com/jrmuizel/raqote.git" diff --git a/shaders/scale.wgsl b/shaders/scale.wgsl index 166b5dfd..d977f201 100644 --- a/shaders/scale.wgsl +++ b/shaders/scale.wgsl @@ -1,18 +1,18 @@ // Vertex shader bindings struct VertexOutput { - [[location(0)]] tex_coord: vec2; - [[builtin(position)]] position: vec4; -}; + @location(0) tex_coord: vec2, + @builtin(position) position: vec4, +} struct Locals { - transform: mat4x4; -}; -[[group(0), binding(2)]] var r_locals: Locals; + transform: mat4x4, +} +@group(0) @binding(2) var r_locals: Locals; -[[stage(vertex)]] +@vertex fn vs_main( - [[location(0)]] position: vec2, + @location(0) position: vec2, ) -> VertexOutput { var out: VertexOutput; out.tex_coord = fma(position, vec2(0.5, -0.5), vec2(0.5, 0.5)); @@ -22,10 +22,10 @@ fn vs_main( // Fragment shader bindings -[[group(0), binding(0)]] var r_tex_color: texture_2d; -[[group(0), binding(1)]] var r_tex_sampler: sampler; +@group(0) @binding(0) var r_tex_color: texture_2d; +@group(0) @binding(1) var r_tex_sampler: sampler; -[[stage(fragment)]] -fn fs_main([[location(0)]] tex_coord: vec2) -> [[location(0)]] vec4 { +@fragment +fn fs_main(@location(0) tex_coord: vec2) -> @location(0) vec4 { return textureSample(r_tex_color, r_tex_sampler, tex_coord); } diff --git a/src/builder.rs b/src/builder.rs index 40d7c4a2..73db59f8 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -296,9 +296,10 @@ impl<'req, 'dev, 'win, W: HasRawWindowHandle> PixelsBuilder<'req, 'dev, 'win, W> let present_mode = self.present_mode; let surface_texture_format = self.surface_texture_format.unwrap_or_else(|| { - surface - .get_preferred_format(&adapter) - .unwrap_or(wgpu::TextureFormat::Bgra8UnormSrgb) + *surface + .get_supported_formats(&adapter) + .first() + .unwrap_or(&wgpu::TextureFormat::Bgra8UnormSrgb) }); let render_texture_format = self.render_texture_format.unwrap_or(surface_texture_format); @@ -531,74 +532,22 @@ const fn get_texture_format_size(texture_format: wgpu::TextureFormat) -> f32 { | EacR11Unorm | EacR11Snorm => 0.5, // 4.0 * 4.0 / 8.0 - // 4x4 blocks, 16 bytes per block - Bc2RgbaUnorm - | Bc2RgbaUnormSrgb - | Bc3RgbaUnorm - | Bc3RgbaUnormSrgb - | Bc5RgUnorm - | Bc5RgSnorm - | Bc6hRgbUfloat - | Bc6hRgbSfloat - | Bc7RgbaUnorm - | Bc7RgbaUnormSrgb - | EacRg11Unorm - | EacRg11Snorm - | Etc2Rgba8Unorm - | Etc2Rgba8UnormSrgb - | Astc4x4RgbaUnorm - | Astc4x4RgbaUnormSrgb => 1.0, // 4.0 * 4.0 / 16.0 - - // 5x4 blocks, 16 bytes per block - Astc5x4RgbaUnorm - | Astc5x4RgbaUnormSrgb => 1.25, // 5.0 * 4.0 / 16.0 - - // 5x5 blocks, 16 bytes per block - Astc5x5RgbaUnorm - | Astc5x5RgbaUnormSrgb => 1.5625, // 5.0 * 5.0 / 16.0 - - // 6x5 blocks, 16 bytes per block - Astc6x5RgbaUnorm - | Astc6x5RgbaUnormSrgb => 1.875, // 6.0 * 5.0 / 16.0 - - // 6x6 blocks, 16 bytes per block - Astc6x6RgbaUnorm - | Astc6x6RgbaUnormSrgb => 2.25, // 6.0 * 6.0 / 16.0 - - // 8x5 blocks, 16 bytes per block - Astc8x5RgbaUnorm - | Astc8x5RgbaUnormSrgb => 2.5, // 8.0 * 5.0 / 16.0 - - // 8x6 blocks, 16 bytes per block - Astc8x6RgbaUnorm - | Astc8x6RgbaUnormSrgb => 3.0, // 8.0 * 6.0 / 16.0 - - // 8x8 blocks, 16 bytes per block - Astc8x8RgbaUnorm - | Astc8x8RgbaUnormSrgb => 4.0, // 8.0 * 8.0 / 16.0 - - // 10x5 blocks, 16 bytes per block - Astc10x5RgbaUnorm - | Astc10x5RgbaUnormSrgb => 3.125, // 10.0 * 5.0 / 16.0 - - // 10x6 blocks, 16 bytes per block - Astc10x6RgbaUnorm - | Astc10x6RgbaUnormSrgb => 3.75, // 10.0 * 6.0 / 16.0 - - // 10x8 blocks, 16 bytes per block - Astc10x8RgbaUnorm - | Astc10x8RgbaUnormSrgb => 5.0, // 10.0 * 8.0 / 16.0 - - // 10x10 blocks, 16 bytes per block - Astc10x10RgbaUnorm - | Astc10x10RgbaUnormSrgb => 6.25, // 10.0 * 10.0 / 16.0 - - // 12x10 blocks, 16 bytes per block - Astc12x10RgbaUnorm - | Astc12x10RgbaUnormSrgb => 7.5, // 12.0 * 10.0 / 16.0 - - // 12x12 blocks, 16 bytes per block - Astc12x12RgbaUnorm - | Astc12x12RgbaUnormSrgb => 9.0, // 12.0 * 12.0 / 16.0 + Depth32FloatStencil8 => todo!(), + Depth24UnormStencil8 => todo!(), + Bc2RgbaUnorm => todo!(), + Bc2RgbaUnormSrgb => todo!(), + Bc3RgbaUnorm => todo!(), + Bc3RgbaUnormSrgb => todo!(), + Bc5RgUnorm => todo!(), + Bc5RgSnorm => todo!(), + Bc6hRgbUfloat => todo!(), + Bc6hRgbSfloat => todo!(), + Bc7RgbaUnorm => todo!(), + Bc7RgbaUnormSrgb => todo!(), + Etc2Rgba8Unorm => todo!(), + Etc2Rgba8UnormSrgb => todo!(), + EacRg11Unorm => todo!(), + EacRg11Snorm => todo!(), + Astc { block, channel } => todo!(), } } diff --git a/src/renderers.rs b/src/renderers.rs index e148c9b7..73c6e0ac 100644 --- a/src/renderers.rs +++ b/src/renderers.rs @@ -26,7 +26,7 @@ impl ScalingRenderer { blend_state: wgpu::BlendState, ) -> Self { let shader = wgpu::include_wgsl!("../shaders/scale.wgsl"); - let module = device.create_shader_module(&shader); + let module = device.create_shader_module(shader); // Create a texture sampler with nearest neighbor let sampler = device.create_sampler(&wgpu::SamplerDescriptor { @@ -151,11 +151,11 @@ impl ScalingRenderer { fragment: Some(wgpu::FragmentState { module: &module, entry_point: "fs_main", - targets: &[wgpu::ColorTargetState { + targets: &[Some(wgpu::ColorTargetState { format: render_texture_format, blend: Some(blend_state), write_mask: wgpu::ColorWrites::ALL, - }], + })], }), multiview: None, }); @@ -179,14 +179,14 @@ impl ScalingRenderer { pub fn render(&self, encoder: &mut wgpu::CommandEncoder, render_target: &wgpu::TextureView) { let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: Some("pixels_scaling_renderer_render_pass"), - color_attachments: &[wgpu::RenderPassColorAttachment { + color_attachments: &[Some(wgpu::RenderPassColorAttachment { view: render_target, resolve_target: None, ops: wgpu::Operations { load: wgpu::LoadOp::Clear(self.clear_color), store: true, }, - }], + })], depth_stencil_attachment: None, }); rpass.set_pipeline(&self.render_pipeline); From 9f27a2f55a4f9fcf57f6c3a66dff5811eb50ff83 Mon Sep 17 00:00:00 2001 From: Jay Oster Date: Sun, 20 Mar 2022 13:24:20 -0700 Subject: [PATCH 05/11] Update egui (#264) --- examples/minimal-egui/src/gui.rs | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/examples/minimal-egui/src/gui.rs b/examples/minimal-egui/src/gui.rs index 016bb7d7..815920cd 100644 --- a/examples/minimal-egui/src/gui.rs +++ b/examples/minimal-egui/src/gui.rs @@ -1,4 +1,4 @@ -use egui::{ClippedMesh, CtxRef}; +use egui::{ClippedMesh, Context, TexturesDelta}; use egui_wgpu_backend::{BackendError, RenderPass, ScreenDescriptor}; use pixels::{wgpu, PixelsContext}; use winit::window::Window; @@ -6,11 +6,12 @@ use winit::window::Window; /// Manages all state required for rendering egui over `Pixels`. pub(crate) struct Framework { // State for egui. - egui_ctx: CtxRef, + egui_ctx: Context, egui_state: egui_winit::State, screen_descriptor: ScreenDescriptor, rpass: RenderPass, paint_jobs: Vec, + textures: TexturesDelta, // State for the GUI gui: Gui, @@ -25,14 +26,17 @@ struct Gui { impl Framework { /// Create egui. pub(crate) fn new(width: u32, height: u32, scale_factor: f32, pixels: &pixels::Pixels) -> Self { - let egui_ctx = CtxRef::default(); - let egui_state = egui_winit::State::from_pixels_per_point(scale_factor); + let max_texture_size = pixels.device().limits().max_texture_dimension_2d as usize; + + let egui_ctx = Context::default(); + let egui_state = egui_winit::State::from_pixels_per_point(max_texture_size, scale_factor); let screen_descriptor = ScreenDescriptor { physical_width: width, physical_height: height, scale_factor, }; let rpass = RenderPass::new(pixels.device(), pixels.render_texture_format(), 1); + let textures = TexturesDelta::default(); let gui = Gui::new(); Self { @@ -41,6 +45,7 @@ impl Framework { screen_descriptor, rpass, paint_jobs: Vec::new(), + textures, gui, } } @@ -67,14 +72,15 @@ impl Framework { pub(crate) fn prepare(&mut self, window: &Window) { // Run the egui frame and create all paint jobs to prepare for rendering. let raw_input = self.egui_state.take_egui_input(window); - let (output, paint_commands) = self.egui_ctx.run(raw_input, |egui_ctx| { + let output = self.egui_ctx.run(raw_input, |egui_ctx| { // Draw the demo application. self.gui.ui(egui_ctx); }); + self.textures.append(output.textures_delta); self.egui_state - .handle_output(window, &self.egui_ctx, output); - self.paint_jobs = self.egui_ctx.tessellate(paint_commands); + .handle_platform_output(window, &self.egui_ctx, output.platform_output); + self.paint_jobs = self.egui_ctx.tessellate(output.shapes); } /// Render egui. @@ -86,9 +92,7 @@ impl Framework { ) -> Result<(), BackendError> { // Upload all resources to the GPU. self.rpass - .update_texture(&context.device, &context.queue, &self.egui_ctx.font_image()); - self.rpass - .update_user_textures(&context.device, &context.queue); + .add_textures(&context.device, &context.queue, &self.textures)?; self.rpass.update_buffers( &context.device, &context.queue, @@ -103,7 +107,11 @@ impl Framework { &self.paint_jobs, &self.screen_descriptor, None, - ) + )?; + + // Cleanup + let textures = std::mem::take(&mut self.textures); + self.rpass.remove_textures(textures) } } @@ -114,7 +122,7 @@ impl Gui { } /// Create the UI using egui. - fn ui(&mut self, ctx: &CtxRef) { + fn ui(&mut self, ctx: &Context) { egui::TopBottomPanel::top("menubar_container").show(ctx, |ui| { egui::menu::bar(ui, |ui| { ui.menu_button("File", |ui| { From 3a02e9a8570a6a9559b6fa9fc0834e3c3badf552 Mon Sep 17 00:00:00 2001 From: Jay Oster Date: Mon, 25 Apr 2022 03:48:35 -0700 Subject: [PATCH 06/11] Fix CI with latest nightly Clippy (#271) --- examples/invaders/simple-invaders/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/invaders/simple-invaders/src/lib.rs b/examples/invaders/simple-invaders/src/lib.rs index 3f1dc02b..9e59ccd8 100644 --- a/examples/invaders/simple-invaders/src/lib.rs +++ b/examples/invaders/simple-invaders/src/lib.rs @@ -620,7 +620,7 @@ fn make_invader_grid(assets: &Assets) -> Vec>> { } fn next_invader<'a>( - invaders: &'a mut Vec>>, + invaders: &'a mut [Vec>], stepper: &mut Point, ) -> (&'a mut Invader, bool) { let mut is_leader = false; From 117986b0d8e7eaaea74c475b3d5f6a758f41b18a Mon Sep 17 00:00:00 2001 From: Jay Oster Date: Mon, 25 Apr 2022 05:04:51 -0700 Subject: [PATCH 07/11] Update dependencies (#272) * Update dependencies - Closes #270 * Unify controls in Invaders example The fire button on gamepads was allowing trapid fire when holding the button. Keyboard controls required the fire key to be released between each shot fired. This commit fixes the difference by making the gamepad fire button act like the keyboard fire key. --- examples/conway/src/main.rs | 4 +-- examples/invaders/src/main.rs | 56 ++++++++++++++--------------------- 2 files changed, 25 insertions(+), 35 deletions(-) diff --git a/examples/conway/src/main.rs b/examples/conway/src/main.rs index 1c4ed1df..3943d100 100644 --- a/examples/conway/src/main.rs +++ b/examples/conway/src/main.rs @@ -66,7 +66,7 @@ fn main() -> Result<(), Error> { if input.key_pressed(VirtualKeyCode::P) { paused = !paused; } - if input.key_pressed(VirtualKeyCode::Space) { + if input.key_pressed_os(VirtualKeyCode::Space) { // Space is frame-step, so ensure we're paused paused = true; } @@ -127,7 +127,7 @@ fn main() -> Result<(), Error> { if let Some(size) = input.window_resized() { pixels.resize_surface(size.width, size.height); } - if !paused || input.key_pressed(VirtualKeyCode::Space) { + if !paused || input.key_pressed_os(VirtualKeyCode::Space) { life.update(); } window.request_redraw(); diff --git a/examples/invaders/src/main.rs b/examples/invaders/src/main.rs index 858ad2d5..6a61ed5e 100644 --- a/examples/invaders/src/main.rs +++ b/examples/invaders/src/main.rs @@ -8,10 +8,7 @@ use pixels::{Error, Pixels, SurfaceTexture}; use simple_invaders::{Controls, Direction, World, FPS, HEIGHT, TIME_STEP, WIDTH}; use std::{env, time::Duration}; use winit::{ - dpi::LogicalSize, - event::{Event, VirtualKeyCode}, - event_loop::EventLoop, - window::WindowBuilder, + dpi::LogicalSize, event::VirtualKeyCode, event_loop::EventLoop, window::WindowBuilder, }; use winit_input_helper::WinitInputHelper; @@ -31,8 +28,6 @@ struct Game { gamepad: Option, /// Game pause state. paused: bool, - /// State for key edge detection. - held: [bool; 2], } impl Game { @@ -45,14 +40,10 @@ impl Game { gilrs: Gilrs::new().unwrap(), // XXX: Don't unwrap. gamepad: None, paused: false, - held: [false; 2], } } - fn update_controls(&mut self, event: &Event<()>) { - // Let winit_input_helper collect events to build its state. - self.input.update(event); - + fn update_controls(&mut self) { // Pump the gilrs event loop and find an active gamepad while let Some(gilrs::Event { id, event, .. }) = self.gilrs.next_event() { let pad = self.gilrs.gamepad(id); @@ -67,17 +58,11 @@ impl Game { self.controls = { // Keyboard controls - let held = [ - self.input.key_held(VirtualKeyCode::Pause), - self.input.key_held(VirtualKeyCode::P), - ]; - let mut left = self.input.key_held(VirtualKeyCode::Left); let mut right = self.input.key_held(VirtualKeyCode::Right); - let mut fire = self.input.key_held(VirtualKeyCode::Space); - let mut pause = (held[0] ^ self.held[0] & held[0]) | (held[1] ^ self.held[1] & held[1]); - - self.held = held; + let mut fire = self.input.key_pressed(VirtualKeyCode::Space); + let mut pause = self.input.key_pressed(VirtualKeyCode::Pause) + | self.input.key_pressed(VirtualKeyCode::P); // GamePad controls if let Some(id) = self.gamepad { @@ -85,7 +70,9 @@ impl Game { left |= gamepad.is_pressed(Button::DPadLeft); right |= gamepad.is_pressed(Button::DPadRight); - fire |= gamepad.is_pressed(Button::South); + fire |= gamepad.button_data(Button::South).map_or(false, |button| { + button.is_pressed() && button.counter() == self.gilrs.counter() + }); pause |= gamepad.button_data(Button::Start).map_or(false, |button| { button.is_pressed() && button.counter() == self.gilrs.counter() }); @@ -166,18 +153,21 @@ fn main() -> Result<(), Error> { } }, |g, event| { - // Update controls - g.game.update_controls(&event); - - // Close events - if g.game.input.key_pressed(VirtualKeyCode::Escape) || g.game.input.quit() { - g.exit(); - return; - } - - // Resize the window - if let Some(size) = g.game.input.window_resized() { - g.game.pixels.resize_surface(size.width, size.height); + // Let winit_input_helper collect events to build its state. + if g.game.input.update(event) { + // Update controls + g.game.update_controls(); + + // Close events + if g.game.input.key_pressed(VirtualKeyCode::Escape) || g.game.input.quit() { + g.exit(); + return; + } + + // Resize the window + if let Some(size) = g.game.input.window_resized() { + g.game.pixels.resize_surface(size.width, size.height); + } } }, ); From ed9fa3a32dc3e6dab73d74a6c4bc2936e78ff761 Mon Sep 17 00:00:00 2001 From: Jay Oster Date: Mon, 25 Apr 2022 07:00:30 -0700 Subject: [PATCH 08/11] Comparison with `softbuffer` (#273) --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 629aef64..014ad11d 100644 --- a/README.md +++ b/README.md @@ -65,3 +65,7 @@ $ RUST_LOG=trace cargo run --package minimal-winit --release --no-default-featur ## Comparison with `minifb` The [`minifb`](https://crates.io/crates/minifb) crate shares some similarities with `pixels`; it also allows rapid prototyping of 2D games and emulators. But it requires the use of its own window/GUI management, event loop, and input handling. One of the disadvantages with the `minifb` approach is the lack of hardware acceleration (except on macOS, which uses Metal but is not configurable). An advantage is that it relies on fewer dependencies. + +## Comparison with `softbuffer` + +There is a more recent project called [`softbuffer`](https://github.com/john01dav/softbuffer). It provides similar capabilities to what `pixels` offers, but is intentionally limited to software-only (not hardware-accelerated) rasterization. From 8335e23d74d53f098b180e5b3a5624504cae7f60 Mon Sep 17 00:00:00 2001 From: Jay Oster Date: Mon, 13 Jun 2022 21:03:06 -0700 Subject: [PATCH 09/11] Update egui to 0.18 (#281) * Update egui to 0.18 - Closes #278 * Bump MSRV to 1.60.0 for egui --- .github/workflows/ci.yml | 4 ++-- MSRV.md | 1 + examples/minimal-egui/src/gui.rs | 30 ++++++++++++++++-------------- examples/minimal-egui/src/main.rs | 2 +- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d669a2b8..85a8649e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: rust: - stable - beta - - 1.57.0 + - 1.60.0 steps: - name: Checkout sources uses: actions/checkout@v2 @@ -73,7 +73,7 @@ jobs: rust: - stable - beta - - 1.57.0 + - 1.60.0 steps: - name: Checkout sources uses: actions/checkout@v2 diff --git a/MSRV.md b/MSRV.md index c21e3a67..e1e73791 100644 --- a/MSRV.md +++ b/MSRV.md @@ -2,6 +2,7 @@ | `pixels` version | `rustc` version | |------------------|-----------------| +| `0.10.0` | `1.60.0` | | `0.9.0` | `1.57.0` | | `0.8.0` | `1.52.0` | | `0.7.0` | `1.52.0` | diff --git a/examples/minimal-egui/src/gui.rs b/examples/minimal-egui/src/gui.rs index 815920cd..b10e59c6 100644 --- a/examples/minimal-egui/src/gui.rs +++ b/examples/minimal-egui/src/gui.rs @@ -1,5 +1,5 @@ -use egui::{ClippedMesh, Context, TexturesDelta}; -use egui_wgpu_backend::{BackendError, RenderPass, ScreenDescriptor}; +use egui::{ClippedPrimitive, Context, TexturesDelta}; +use egui_wgpu::renderer::{RenderPass, ScreenDescriptor}; use pixels::{wgpu, PixelsContext}; use winit::window::Window; @@ -10,7 +10,7 @@ pub(crate) struct Framework { egui_state: egui_winit::State, screen_descriptor: ScreenDescriptor, rpass: RenderPass, - paint_jobs: Vec, + paint_jobs: Vec, textures: TexturesDelta, // State for the GUI @@ -31,9 +31,8 @@ impl Framework { let egui_ctx = Context::default(); let egui_state = egui_winit::State::from_pixels_per_point(max_texture_size, scale_factor); let screen_descriptor = ScreenDescriptor { - physical_width: width, - physical_height: height, - scale_factor, + size_in_pixels: [width, height], + pixels_per_point: scale_factor, }; let rpass = RenderPass::new(pixels.device(), pixels.render_texture_format(), 1); let textures = TexturesDelta::default(); @@ -58,14 +57,13 @@ impl Framework { /// Resize egui. pub(crate) fn resize(&mut self, width: u32, height: u32) { if width > 0 && height > 0 { - self.screen_descriptor.physical_width = width; - self.screen_descriptor.physical_height = height; + self.screen_descriptor.size_in_pixels = [width, height]; } } /// Update scaling factor. pub(crate) fn scale_factor(&mut self, scale_factor: f64) { - self.screen_descriptor.scale_factor = scale_factor as f32; + self.screen_descriptor.pixels_per_point = scale_factor as f32; } /// Prepare egui. @@ -89,10 +87,12 @@ impl Framework { encoder: &mut wgpu::CommandEncoder, render_target: &wgpu::TextureView, context: &PixelsContext, - ) -> Result<(), BackendError> { + ) { // Upload all resources to the GPU. - self.rpass - .add_textures(&context.device, &context.queue, &self.textures)?; + for (id, image_delta) in &self.textures.set { + self.rpass + .update_texture(&context.device, &context.queue, *id, image_delta); + } self.rpass.update_buffers( &context.device, &context.queue, @@ -107,11 +107,13 @@ impl Framework { &self.paint_jobs, &self.screen_descriptor, None, - )?; + ); // Cleanup let textures = std::mem::take(&mut self.textures); - self.rpass.remove_textures(textures) + for id in &textures.free { + self.rpass.free_texture(id); + } } } diff --git a/examples/minimal-egui/src/main.rs b/examples/minimal-egui/src/main.rs index 9086b09d..3bba776e 100644 --- a/examples/minimal-egui/src/main.rs +++ b/examples/minimal-egui/src/main.rs @@ -94,7 +94,7 @@ fn main() -> Result<(), Error> { context.scaling_renderer.render(encoder, render_target); // Render egui - framework.render(encoder, render_target, context)?; + framework.render(encoder, render_target, context); Ok(()) }); From 2618566b71a897dfc2ae18168f5418dad17c8f0d Mon Sep 17 00:00:00 2001 From: Jay Oster Date: Mon, 20 Jun 2022 10:12:30 -0700 Subject: [PATCH 10/11] Fix custom-shader example on macOS (#286) - Fixes #258 --- examples/custom-shader/src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/custom-shader/src/main.rs b/examples/custom-shader/src/main.rs index 12938e57..537e89b4 100644 --- a/examples/custom-shader/src/main.rs +++ b/examples/custom-shader/src/main.rs @@ -38,14 +38,14 @@ fn main() -> Result<(), Error> { .unwrap() }; + let window_size = window.inner_size(); let mut pixels = { - let window_size = window.inner_size(); let surface_texture = SurfaceTexture::new(window_size.width, window_size.height, &window); Pixels::new(WIDTH, HEIGHT, surface_texture)? }; let mut world = World::new(); let mut time = 0.0; - let mut noise_renderer = NoiseRenderer::new(&pixels, WIDTH, HEIGHT); + let mut noise_renderer = NoiseRenderer::new(&pixels, window_size.width, window_size.height); event_loop.run(move |event, _, control_flow| { // Draw the current frame From 0b15a5dbf890ffc2f9b665598feebb3aa4f7bd6a Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Sun, 17 Jul 2022 17:29:49 -0700 Subject: [PATCH 11/11] WIP: Update egui --- examples/minimal-egui/src/gui.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/examples/minimal-egui/src/gui.rs b/examples/minimal-egui/src/gui.rs index b10e59c6..4faae44b 100644 --- a/examples/minimal-egui/src/gui.rs +++ b/examples/minimal-egui/src/gui.rs @@ -1,5 +1,5 @@ use egui::{ClippedPrimitive, Context, TexturesDelta}; -use egui_wgpu::renderer::{RenderPass, ScreenDescriptor}; +use egui_wgpu_backend::{RenderPass, ScreenDescriptor}; use pixels::{wgpu, PixelsContext}; use winit::window::Window; @@ -31,8 +31,9 @@ impl Framework { let egui_ctx = Context::default(); let egui_state = egui_winit::State::from_pixels_per_point(max_texture_size, scale_factor); let screen_descriptor = ScreenDescriptor { - size_in_pixels: [width, height], - pixels_per_point: scale_factor, + physical_width: width, + physical_height: height, + scale_factor, }; let rpass = RenderPass::new(pixels.device(), pixels.render_texture_format(), 1); let textures = TexturesDelta::default(); @@ -57,13 +58,14 @@ impl Framework { /// Resize egui. pub(crate) fn resize(&mut self, width: u32, height: u32) { if width > 0 && height > 0 { - self.screen_descriptor.size_in_pixels = [width, height]; + self.screen_descriptor.physical_width = width; + self.screen_descriptor.physical_height = height; } } /// Update scaling factor. pub(crate) fn scale_factor(&mut self, scale_factor: f64) { - self.screen_descriptor.pixels_per_point = scale_factor as f32; + self.screen_descriptor.scale_factor = scale_factor as f32; } /// Prepare egui. @@ -112,7 +114,7 @@ impl Framework { // Cleanup let textures = std::mem::take(&mut self.textures); for id in &textures.free { - self.rpass.free_texture(id); + self.rpass.remove_textures(id); } } }