From 7724df88a12acd3b56b358a359ce2c804e525a63 Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Sun, 15 May 2022 23:08:33 +1000 Subject: [PATCH 1/6] Update `nannou_wgpu`'s wgpu dependency from 0.11 to 0.12 --- nannou_wgpu/Cargo.toml | 2 +- nannou_wgpu/src/bind_group_builder.rs | 40 ++++++++++++++-------- nannou_wgpu/src/lib.rs | 24 ++++++------- nannou_wgpu/src/render_pipeline_builder.rs | 26 ++++++++++---- nannou_wgpu/src/texture/reshaper/mod.rs | 28 +++++++++------ 5 files changed, 76 insertions(+), 44 deletions(-) diff --git a/nannou_wgpu/Cargo.toml b/nannou_wgpu/Cargo.toml index a2057ca71..e2b0b3f68 100644 --- a/nannou_wgpu/Cargo.toml +++ b/nannou_wgpu/Cargo.toml @@ -14,7 +14,7 @@ async-std = "1.10.0" image = { version = "0.23", optional = true } instant = { version = "0.1.9", optional = true } num_cpus = { version = "1", optional = true } -wgpu_upstream = { version = "0.11.1", package = "wgpu" } +wgpu_upstream = { version = "0.12.0", package = "wgpu" } [features] capturer = ["image", "instant", "num_cpus"] diff --git a/nannou_wgpu/src/bind_group_builder.rs b/nannou_wgpu/src/bind_group_builder.rs index 233a36d03..dc3c63bc1 100644 --- a/nannou_wgpu/src/bind_group_builder.rs +++ b/nannou_wgpu/src/bind_group_builder.rs @@ -58,24 +58,34 @@ impl LayoutBuilder { self.binding(visibility, ty) } - /// Add a sampler binding to the layout. - pub fn sampler(self, visibility: wgpu::ShaderStages, filtering: bool) -> Self { - let comparison = false; - let ty = wgpu::BindingType::Sampler { - filtering, - comparison, - }; + /// Add a sampler binding of the given type to the layout. + pub fn sampler(self, visibility: wgpu::ShaderStages, ty: wgpu::SamplerBindingType) -> Self { + let ty = wgpu::BindingType::Sampler(ty); self.binding(visibility, ty) } - /// Add a sampler binding to the layout. - pub fn comparison_sampler(self, visibility: wgpu::ShaderStages, filtering: bool) -> Self { - let comparison = true; - let ty = wgpu::BindingType::Sampler { - filtering, - comparison, - }; - self.binding(visibility, ty) + /// Add a comparison sampler binding to the layout. + /// + /// Use as a comparison sampler instead of a normal sampler. For more info take a look at the + /// analogous functionality in OpenGL: + /// https://www.khronos.org/opengl/wiki/Sampler_Object#Comparison_mode. + pub fn comparison_sampler(self, visibility: wgpu::ShaderStages) -> Self { + self.sampler(visibility, wgpu::SamplerBindingType::Comparison) + } + + /// Add a filtering sampler binding to the layout. + /// + /// Used when the sampling result is produced based on more than a single color sample from a + /// texture, e.g. when bilinear interpolation is enabled. + pub fn filtering_sampler(self, visibility: wgpu::ShaderStages) -> Self { + self.sampler(visibility, wgpu::SamplerBindingType::Filtering) + } + + /// Add a filtering sampler binding to the layout. + /// + /// The sampling result is produced based on a single color sample from a texture. + pub fn non_filtering_sampler(self, visibility: wgpu::ShaderStages) -> Self { + self.sampler(visibility, wgpu::SamplerBindingType::NonFiltering) } /// Add a texture binding to the layout. diff --git a/nannou_wgpu/src/lib.rs b/nannou_wgpu/src/lib.rs index b2bbe29af..6767d18f7 100644 --- a/nannou_wgpu/src/lib.rs +++ b/nannou_wgpu/src/lib.rs @@ -78,7 +78,7 @@ pub use wgpu_upstream::{ CommandEncoder, CommandEncoderDescriptor, CompareFunction, ComputePass, ComputePassDescriptor, ComputePipeline, ComputePipelineDescriptor, DepthBiasState, DepthStencilState, Device, DeviceDescriptor, DeviceType, DownlevelCapabilities, DownlevelFlags, DynamicOffset, Error, - Extent3d, Face, Features, FilterMode, FragmentState, FrontFace, ImageCopyBuffer, + ErrorFilter, Extent3d, Face, Features, FilterMode, FragmentState, FrontFace, ImageCopyBuffer, ImageCopyBufferBase, ImageCopyTexture, ImageCopyTextureBase, ImageDataLayout, ImageSubresourceRange, IndexFormat, Instance, Label, Limits, LoadOp, Maintain, MapMode, MultisampleState, Operations, Origin3d, PipelineLayout, PipelineLayoutDescriptor, @@ -88,16 +88,16 @@ pub use wgpu_upstream::{ RenderBundleEncoderDescriptor, RenderPass, RenderPassColorAttachment, RenderPassDepthStencilAttachment, RenderPassDescriptor, RenderPipeline, RenderPipelineDescriptor, RequestAdapterOptions, RequestAdapterOptionsBase, RequestDeviceError, - Sampler, SamplerBorderColor, SamplerDescriptor, ShaderLocation, ShaderModel, ShaderModule, - ShaderModuleDescriptor, ShaderSource, ShaderStages, StencilFaceState, StencilOperation, - StencilState, StorageTextureAccess, Surface, SurfaceConfiguration, SurfaceError, SurfaceStatus, - SurfaceTexture, Texture as TextureHandle, TextureAspect, TextureDescriptor, TextureDimension, - TextureFormat, TextureFormatFeatureFlags, TextureFormatFeatures, TextureSampleType, - TextureUsages, TextureView as TextureViewHandle, TextureViewDescriptor, TextureViewDimension, - UncapturedErrorHandler, VertexAttribute, VertexBufferLayout, VertexFormat, VertexState, - VertexStepMode, COPY_BUFFER_ALIGNMENT, COPY_BYTES_PER_ROW_ALIGNMENT, MAP_ALIGNMENT, - PUSH_CONSTANT_ALIGNMENT, QUERY_RESOLVE_BUFFER_ALIGNMENT, QUERY_SET_MAX_QUERIES, QUERY_SIZE, - VERTEX_STRIDE_ALIGNMENT, + Sampler, SamplerBindingType, SamplerBorderColor, SamplerDescriptor, ShaderLocation, + ShaderModel, ShaderModule, ShaderModuleDescriptor, ShaderModuleDescriptorSpirV, ShaderSource, + ShaderStages, StencilFaceState, StencilOperation, StencilState, StorageTextureAccess, Surface, + SurfaceConfiguration, SurfaceError, SurfaceStatus, SurfaceTexture, Texture as TextureHandle, + TextureAspect, TextureDescriptor, TextureDimension, TextureFormat, TextureFormatFeatureFlags, + TextureFormatFeatures, TextureSampleType, TextureUsages, TextureView as TextureViewHandle, + TextureViewDescriptor, TextureViewDimension, UncapturedErrorHandler, VertexAttribute, + VertexBufferLayout, VertexFormat, VertexState, VertexStepMode, COPY_BUFFER_ALIGNMENT, + COPY_BYTES_PER_ROW_ALIGNMENT, MAP_ALIGNMENT, PUSH_CONSTANT_ALIGNMENT, + QUERY_RESOLVE_BUFFER_ALIGNMENT, QUERY_SET_MAX_QUERIES, QUERY_SIZE, VERTEX_STRIDE_ALIGNMENT, }; /// The default power preference used for requesting the WGPU adapter. @@ -186,7 +186,7 @@ pub fn create_pipeline_layout<'p>( /// Whether or not the sampler descriptor describes a sampler that might perform linear filtering. /// -/// This is used to determine the `filtering` field for the sampler binding type variant which +/// This is used to determine the `SamplerBindingType` for the sampler binding variant which /// assists wgpu with validation. pub fn sampler_filtering(desc: &SamplerDescriptor) -> bool { match (desc.mag_filter, desc.min_filter, desc.mipmap_filter) { diff --git a/nannou_wgpu/src/render_pipeline_builder.rs b/nannou_wgpu/src/render_pipeline_builder.rs index cf9d97169..f1f17108e 100644 --- a/nannou_wgpu/src/render_pipeline_builder.rs +++ b/nannou_wgpu/src/render_pipeline_builder.rs @@ -34,6 +34,7 @@ pub struct RenderPipelineBuilder<'a> { depth_stencil: Option, vertex_buffers: Vec>, multisample: wgpu::MultisampleState, + multiview: Option, } impl<'a> RenderPipelineBuilder<'a> { @@ -52,7 +53,7 @@ impl<'a> RenderPipelineBuilder<'a> { front_face: Self::DEFAULT_FRONT_FACE, cull_mode: Self::DEFAULT_CULL_MODE, polygon_mode: Self::DEFAULT_POLYGON_MODE, - clamp_depth: Self::DEFAULT_CLAMP_DEPTH, + unclipped_depth: false, conservative: false, }; @@ -101,7 +102,6 @@ impl<'a> RenderPipelineBuilder<'a> { slope_scale: Self::DEFAULT_DEPTH_BIAS_SLOPE_SCALE, clamp: Self::DEFAULT_DEPTH_BIAS_CLAMP, }; - pub const DEFAULT_CLAMP_DEPTH: bool = false; pub const DEFAULT_DEPTH_STENCIL: wgpu::DepthStencilState = wgpu::DepthStencilState { format: Self::DEFAULT_DEPTH_FORMAT, depth_write_enabled: Self::DEFAULT_DEPTH_WRITE_ENABLED, @@ -154,6 +154,7 @@ impl<'a> RenderPipelineBuilder<'a> { depth_stencil: None, vertex_buffers: vec![], multisample: Self::DEFAULT_MULTISAMPLE, + multiview: None, } } @@ -385,11 +386,11 @@ impl<'a> RenderPipelineBuilder<'a> { self } - /// If enabled polygon depth is clamped to 0-1 range instead of being clipped. + /// If set to true, the polygon depth is not clipped to 0-1 before rasterization. /// - /// Requires `Features::DEPTH_CLAMPING` enabled. - pub fn clamp_depth(mut self, b: bool) -> Self { - self.primitive.clamp_depth = b; + /// Enabling this requires `Features::DEPTH_CLIP_CONTROL` to be enabled. + pub fn unclipped_depth(mut self, b: bool) -> Self { + self.primitive.unclipped_depth = b; self } @@ -466,6 +467,17 @@ impl<'a> RenderPipelineBuilder<'a> { self } + // Multiview. + + /// If the pipeline will be used with a multiview render pass, this indicates how many array + /// layers the attachments will have. + /// + /// `None` by default. + pub fn multiview(mut self, layers: Option) -> Self { + self.multiview = layers; + self + } + // Finalising methods. /// Build the render pipeline layout, its descriptor and ultimately the pipeline itself with @@ -519,6 +531,7 @@ fn build( depth_stencil, multisample, vertex_buffers, + multiview, } = builder; let vertex = wgpu::VertexState { @@ -559,6 +572,7 @@ fn build( depth_stencil, multisample, fragment, + multiview, }; device.create_render_pipeline(&pipeline_desc) diff --git a/nannou_wgpu/src/texture/reshaper/mod.rs b/nannou_wgpu/src/texture/reshaper/mod.rs index 0a2a5521b..5ea782b5d 100644 --- a/nannou_wgpu/src/texture/reshaper/mod.rs +++ b/nannou_wgpu/src/texture/reshaper/mod.rs @@ -10,11 +10,11 @@ use crate::{self as wgpu, util::DeviceExt, BufferInitDescriptor}; pub struct Reshaper { _vs_mod: wgpu::ShaderModule, _fs_mod: wgpu::ShaderModule, - bind_group_layout: wgpu::BindGroupLayout, + _bind_group_layout: wgpu::BindGroupLayout, bind_group: wgpu::BindGroup, render_pipeline: wgpu::RenderPipeline, - sampler: wgpu::Sampler, - uniform_buffer: Option, + _sampler: wgpu::Sampler, + _uniform_buffer: Option, vertex_buffer: wgpu::Buffer, } @@ -56,11 +56,19 @@ impl Reshaper { // Create the sampler for sampling from the source texture. let sampler_desc = wgpu::SamplerBuilder::new().into_descriptor(); let sampler_filtering = wgpu::sampler_filtering(&sampler_desc); + let sampler_binding_ty = match sampler_filtering { + true => wgpu::SamplerBindingType::Filtering, + false => wgpu::SamplerBindingType::NonFiltering, + }; let sampler = device.create_sampler(&sampler_desc); // Create the render pipeline. - let bind_group_layout = - bind_group_layout(device, src_sample_count, src_sample_type, sampler_filtering); + let bind_group_layout = bind_group_layout( + device, + src_sample_count, + src_sample_type, + sampler_binding_ty, + ); let pipeline_layout = pipeline_layout(device, &bind_group_layout); let render_pipeline = render_pipeline( device, @@ -111,11 +119,11 @@ impl Reshaper { Reshaper { _vs_mod: vs_mod, _fs_mod: fs_mod, - bind_group_layout, + _bind_group_layout: bind_group_layout, bind_group, render_pipeline, - sampler, - uniform_buffer, + _sampler: sampler, + _uniform_buffer: uniform_buffer, vertex_buffer, } } @@ -166,7 +174,7 @@ fn bind_group_layout( device: &wgpu::Device, src_sample_count: u32, src_sample_type: wgpu::TextureSampleType, - sampler_filtering: bool, + sampler_binding_ty: wgpu::SamplerBindingType, ) -> wgpu::BindGroupLayout { let mut builder = wgpu::BindGroupLayoutBuilder::new() .texture( @@ -175,7 +183,7 @@ fn bind_group_layout( wgpu::TextureViewDimension::D2, src_sample_type, ) - .sampler(wgpu::ShaderStages::FRAGMENT, sampler_filtering); + .sampler(wgpu::ShaderStages::FRAGMENT, sampler_binding_ty); if !unrolled_sample_count(src_sample_count) { builder = builder.uniform_buffer(wgpu::ShaderStages::FRAGMENT, false); } From 5f6e6589deebdbb59c13ea83035fef5d5a8cb1d9 Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Mon, 16 May 2022 00:05:04 +1000 Subject: [PATCH 2/6] Update nannou crate for wgpu version 0.12 Includes removing the unnecessary block attr from nannou draw wgsl vertex shader --- nannou/Cargo.toml | 2 +- nannou/src/draw/renderer/mod.rs | 29 ++++++++++++++++-------- nannou/src/draw/renderer/shaders/vs.wgsl | 1 - 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/nannou/Cargo.toml b/nannou/Cargo.toml index 3d50152bf..a7e55c9fe 100644 --- a/nannou/Cargo.toml +++ b/nannou/Cargo.toml @@ -31,7 +31,7 @@ serde_json = "1" toml = "0.5" walkdir = "2" web-sys = { version = "0.3.55", optional = true } -wgpu_upstream = { version = "0.11.1", package = "wgpu" } +wgpu_upstream = { version = "0.12.0", package = "wgpu" } winit = "0.26" [features] diff --git a/nannou/src/draw/renderer/mod.rs b/nannou/src/draw/renderer/mod.rs index 7c978435e..926d18d92 100644 --- a/nannou/src/draw/renderer/mod.rs +++ b/nannou/src/draw/renderer/mod.rs @@ -82,7 +82,7 @@ pub struct Renderer { glyph_cache_texture: wgpu::Texture, depth_texture: wgpu::Texture, depth_texture_view: wgpu::TextureView, - default_texture: wgpu::Texture, + _default_texture: wgpu::Texture, default_texture_view: wgpu::TextureView, uniform_bind_group_layout: wgpu::BindGroupLayout, uniform_bind_group: wgpu::BindGroup, @@ -393,6 +393,7 @@ impl Renderer { // Create the glyph cache texture. let text_sampler_desc = wgpu::SamplerBuilder::new().into_descriptor(); let text_sampler_filtering = wgpu::sampler_filtering(&text_sampler_desc); + let text_sampler_binding_ty = sampler_binding_type(text_sampler_filtering); let text_sampler = device.create_sampler(&text_sampler_desc); let glyph_cache_texture = wgpu::TextureBuilder::new() .size(glyph_cache_size) @@ -430,7 +431,7 @@ impl Renderer { create_uniform_bind_group(device, &uniform_bind_group_layout, &uniform_buffer); // Bind group for text. - let text_bind_group_layout = create_text_bind_group_layout(device, text_sampler_filtering); + let text_bind_group_layout = create_text_bind_group_layout(device, text_sampler_binding_ty); let text_bind_group = create_text_bind_group( device, &text_bind_group_layout, @@ -462,7 +463,7 @@ impl Renderer { glyph_cache_texture, depth_texture, depth_texture_view, - default_texture, + _default_texture: default_texture, default_texture_view, uniform_bind_group_layout, uniform_bind_group, @@ -647,9 +648,10 @@ impl Renderer { let color_blend = curr_ctxt.blend.color.clone(); let alpha_blend = curr_ctxt.blend.alpha.clone(); let sampler_filtering = wgpu::sampler_filtering(&curr_ctxt.sampler); + let sampler_binding_ty = sampler_binding_type(sampler_filtering); new_pipeline_ids.insert( new_pipeline_id, - (color_blend, alpha_blend, sampler_filtering), + (color_blend, alpha_blend, sampler_binding_ty), ); let cmd = RenderCommand::SetPipeline(new_pipeline_id); self.render_commands.push(cmd); @@ -707,14 +709,14 @@ impl Renderer { // Clear new combos that we already have. new_pipeline_ids.retain(|id, _| !self.pipelines.contains_key(id)); // Create new render pipelines as necessary. - for (new_id, (color_blend, alpha_blend, sampler_filtering)) in new_pipeline_ids { + for (new_id, (color_blend, alpha_blend, sampler_binding_ty)) in new_pipeline_ids { let bind_group_layout = self .texture_bind_group_layouts .entry(new_id.texture_sample_type) .or_insert_with(|| { create_texture_bind_group_layout( device, - sampler_filtering, + sampler_binding_ty, new_id.texture_sample_type, ) }); @@ -1045,9 +1047,9 @@ fn create_uniform_bind_group_layout(device: &wgpu::Device) -> wgpu::BindGroupLay .build(device) } -fn create_text_bind_group_layout(device: &wgpu::Device, filtering: bool) -> wgpu::BindGroupLayout { +fn create_text_bind_group_layout(device: &wgpu::Device, sampler_ty: wgpu::SamplerBindingType) -> wgpu::BindGroupLayout { wgpu::BindGroupLayoutBuilder::new() - .sampler(wgpu::ShaderStages::FRAGMENT, filtering) + .sampler(wgpu::ShaderStages::FRAGMENT, sampler_ty) .texture( wgpu::ShaderStages::FRAGMENT, false, @@ -1057,13 +1059,20 @@ fn create_text_bind_group_layout(device: &wgpu::Device, filtering: bool) -> wgpu .build(device) } +fn sampler_binding_type(filtering: bool) -> wgpu::SamplerBindingType { + match filtering { + true => wgpu::SamplerBindingType::Filtering, + false => wgpu::SamplerBindingType::NonFiltering, + } +} + fn create_texture_bind_group_layout( device: &wgpu::Device, - filtering: bool, + sampler_binding_ty: wgpu::SamplerBindingType, texture_sample_type: wgpu::TextureSampleType, ) -> wgpu::BindGroupLayout { wgpu::BindGroupLayoutBuilder::new() - .sampler(wgpu::ShaderStages::FRAGMENT, filtering) + .sampler(wgpu::ShaderStages::FRAGMENT, sampler_binding_ty) .texture( wgpu::ShaderStages::FRAGMENT, false, diff --git a/nannou/src/draw/renderer/shaders/vs.wgsl b/nannou/src/draw/renderer/shaders/vs.wgsl index c8d8923c0..c7837e489 100644 --- a/nannou/src/draw/renderer/shaders/vs.wgsl +++ b/nannou/src/draw/renderer/shaders/vs.wgsl @@ -1,4 +1,3 @@ -[[block]] struct Data { proj: mat4x4; }; From 4c37f0722e0be19a7aaccf2d4d20ef4797df0f73 Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Sun, 29 May 2022 16:22:58 +1000 Subject: [PATCH 3/6] Update nannou_isf for wgpu 0.12 --- nannou_isf/src/pipeline.rs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/nannou_isf/src/pipeline.rs b/nannou_isf/src/pipeline.rs index 3f81a3adf..2c38d7d9c 100644 --- a/nannou_isf/src/pipeline.rs +++ b/nannou_isf/src/pipeline.rs @@ -16,7 +16,7 @@ pub struct IsfPipeline { vs: Shader, fs: Shader, sampler: wgpu::Sampler, - sampler_filtering: bool, + sampler_binding_type: wgpu::SamplerBindingType, isf_uniform_buffer: wgpu::Buffer, isf_inputs_uniform_buffer: wgpu::Buffer, isf_bind_group_layout: wgpu::BindGroupLayout, @@ -550,6 +550,7 @@ impl IsfPipeline { // Create the sampler. let sampler_desc = wgpu::SamplerBuilder::new().into_descriptor(); let sampler_filtering = wgpu::sampler_filtering(&sampler_desc); + let sampler_binding_type = sampler_binding_type(sampler_filtering); let sampler = device.create_sampler(&sampler_desc); // Prepare the bind group layouts. @@ -560,7 +561,7 @@ impl IsfPipeline { .uniform_buffer(wgpu::ShaderStages::FRAGMENT, false) .build(device); let isf_textures_bind_group_layout = - create_isf_textures_bind_group_layout(device, sampler_filtering, &isf_data); + create_isf_textures_bind_group_layout(device, sampler_binding_type, &isf_data); // Create the bind groups let isf_bind_group = wgpu::BindGroupBuilder::new() @@ -614,7 +615,7 @@ impl IsfPipeline { vs, fs, sampler, - sampler_filtering, + sampler_binding_type, isf_uniform_buffer, isf_inputs_uniform_buffer, isf_bind_group_layout, @@ -716,7 +717,7 @@ impl IsfPipeline { if texture_count_changed { self.isf_textures_bind_group_layout = create_isf_textures_bind_group_layout( device, - self.sampler_filtering, + self.sampler_binding_type, &self.isf_data, ); self.isf_textures_bind_group = create_isf_textures_bind_group( @@ -842,12 +843,12 @@ fn split_result(res: Result) -> (Option, Option) { // Includes the sampler and then all textures for all images and passes. fn create_isf_textures_bind_group_layout( device: &wgpu::Device, - sampler_filtering: bool, + sampler_binding_type: wgpu::SamplerBindingType, isf_data: &IsfData, ) -> wgpu::BindGroupLayout { // Begin with the sampler. let mut builder = wgpu::BindGroupLayoutBuilder::new() - .sampler(wgpu::ShaderStages::FRAGMENT, sampler_filtering); + .sampler(wgpu::ShaderStages::FRAGMENT, sampler_binding_type); for texture in isf_data_textures(isf_data) { builder = builder.texture( wgpu::ShaderStages::FRAGMENT, @@ -999,6 +1000,13 @@ fn default_isf_texture_usage() -> wgpu::TextureUsages { wgpu::TextureUsages::COPY_DST | wgpu::TextureUsages::TEXTURE_BINDING } +fn sampler_binding_type(filtering: bool) -> wgpu::SamplerBindingType { + match filtering { + true => wgpu::SamplerBindingType::Filtering, + false => wgpu::SamplerBindingType::NonFiltering, + } +} + fn read_isf_from_path(path: &Path) -> Result { std::fs::read_to_string(path) .map_err(|err| IsfError::from(err)) From d69610be3e7b6fe564d2e980e18885fa82d11b97 Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Mon, 16 May 2022 00:05:22 +1000 Subject: [PATCH 4/6] WIP: Updating nannou_egui and demo app to wgpu 0.12, egui 0.17 WIP as demo app immediately `panic!`s with a wgpu error when running. --- nannou_egui/Cargo.toml | 5 ++- nannou_egui/src/lib.rs | 74 ++++++++++++++++---------------- nannou_egui_demo_app/Cargo.toml | 2 +- nannou_egui_demo_app/src/main.rs | 2 +- 4 files changed, 42 insertions(+), 41 deletions(-) diff --git a/nannou_egui/Cargo.toml b/nannou_egui/Cargo.toml index b2776bff2..1e8febd27 100644 --- a/nannou_egui/Cargo.toml +++ b/nannou_egui/Cargo.toml @@ -12,7 +12,8 @@ repository = "https://github.com/AlexEne/nannou_egui" readme = "../README.md" [dependencies] -egui_wgpu_backend = "0.14" -egui = "0.15.0" +egui_wgpu_backend = "0.17" +egui = "0.17" +epi = "0.17" winit = "0.26" nannou = { version = "0.18.1", path = "../nannou" } diff --git a/nannou_egui/src/lib.rs b/nannou_egui/src/lib.rs index cefdc1b8b..ce01afe6d 100644 --- a/nannou_egui/src/lib.rs +++ b/nannou_egui/src/lib.rs @@ -1,9 +1,10 @@ pub use egui; pub use egui::color_picker; pub use egui_wgpu_backend; +pub use epi; -use egui::{pos2, ClippedMesh, CtxRef}; -use egui_wgpu_backend::{epi, ScreenDescriptor}; +use egui::{pos2, ClippedMesh, Context}; +use egui_wgpu_backend::ScreenDescriptor; use nannou::{wgpu, winit::event::VirtualKeyCode, winit::event::WindowEvent::*}; use std::{ cell::RefCell, @@ -18,7 +19,7 @@ use std::{ /// /// For multi-window user interfaces, you will need to create an instance of this type per-window. pub struct Egui { - context: CtxRef, + context: Context, renderer: RefCell, input: Input, } @@ -30,6 +31,7 @@ pub struct Egui { pub struct Renderer { render_pass: egui_wgpu_backend::RenderPass, paint_jobs: Vec, + textures_delta: egui::TexturesDelta, } /// Tracking user and application event input. @@ -40,7 +42,7 @@ pub struct Input { pub window_scale_factor: f32, } -/// A wrapper around a `CtxRef` on which `begin_frame` was called. +/// A wrapper around a `Context` on which `begin_frame` was called. /// /// Automatically calls `end_frame` on `drop` in the case that it wasn't already called by the /// usef. @@ -88,8 +90,8 @@ impl Egui { Self::new(device, format, msaa_samples, scale_factor, [w_px, h_px]) } - /// Access to the inner `egui::CtxRef`. - pub fn ctx(&self) -> &CtxRef { + /// Access to the inner `egui::Context`. + pub fn ctx(&self) -> &Context { &self.context } @@ -120,7 +122,7 @@ impl Egui { pub fn texture_from_wgpu_texture( &mut self, device: &wgpu::Device, - texture: &wgpu::Texture, + texture: &wgpu::TextureView, texture_filter: wgpu::FilterMode, ) -> egui::TextureId { self.renderer @@ -133,7 +135,7 @@ impl Egui { pub fn update_texture_from_wgpu_texture( &mut self, device: &wgpu::Device, - texture: &wgpu::Texture, + texture: &wgpu::TextureView, texture_filter: wgpu::FilterMode, id: egui::TextureId, ) -> Result<(), egui_wgpu_backend::BackendError> { @@ -149,7 +151,7 @@ impl Egui { frame: &nannou::Frame, ) -> Result<(), egui_wgpu_backend::BackendError> { let mut renderer = self.renderer.borrow_mut(); - renderer.draw_to_frame(&self.context, frame) + renderer.draw_to_frame(frame) } /// Provide access to an `epi::Frame` within the given function. @@ -157,9 +159,8 @@ impl Egui { /// This method is primarily used for apps based on the `epi` interface. pub fn with_epi_frame(&mut self, proxy: nannou::app::Proxy, f: F) where - F: FnOnce(&CtxRef, &mut epi::Frame), + F: FnOnce(&Context, &mut epi::Frame), { - let mut renderer = self.renderer.borrow_mut(); let integration_info = epi::IntegrationInfo { native_pixels_per_point: Some(self.input.window_scale_factor as _), // TODO: Provide access to this stuff. @@ -168,26 +169,22 @@ impl Egui { cpu_usage: None, name: "egui_nannou_wgpu", }; - let mut app_output = epi::backend::AppOutput::default(); + let app_output = epi::backend::AppOutput::default(); let repaint_signal = Arc::new(RepaintSignal(Mutex::new(proxy))); - let mut frame = epi::backend::FrameBuilder { + let frame_data = epi::backend::FrameData { info: integration_info, - tex_allocator: &mut renderer.render_pass, - // TODO: We may want to support a http feature for hyperlinks? - // #[cfg(feature = "http")] - // http: http.clone(), - output: &mut app_output, + output: app_output, repaint_signal: repaint_signal as Arc<_>, - } - .build(); + }; + let mut frame = epi::Frame(Arc::new(Mutex::new(frame_data))); f(&self.context, &mut frame) } /// The same as `with_epi_frame`, but calls `begin_frame` before calling the given function, /// and then calls `end_frame` before returning. - pub fn do_frame_with_epi_frame(&mut self, proxy: nannou::app::Proxy, f: F) -> egui::Output + pub fn do_frame_with_epi_frame(&mut self, proxy: nannou::app::Proxy, f: F) -> egui::FullOutput where - F: FnOnce(&CtxRef, &mut epi::Frame), + F: FnOnce(&Context, &mut epi::Frame), { self.begin_frame_inner(); self.with_epi_frame(proxy.clone(), f); @@ -205,9 +202,11 @@ impl Egui { self.context.begin_frame(self.input.raw.take()); } - fn end_frame_inner(&mut self) -> egui::Output { - let (output, paint_cmds) = self.context.end_frame(); - self.renderer.borrow_mut().paint_jobs = self.context.tessellate(paint_cmds); + fn end_frame_inner(&mut self) -> egui::FullOutput { + let output = self.context.end_frame(); + let mut renderer = self.renderer.borrow_mut(); + renderer.paint_jobs = self.context.tessellate(output.shapes.clone()); + renderer.textures_delta = output.textures_delta.clone(); output } } @@ -267,11 +266,13 @@ impl Input { match delta { winit::event::MouseScrollDelta::LineDelta(x, y) => { let line_height = 24.0; - self.raw.scroll_delta = egui::vec2(*x, *y) * line_height; + let scroll_delta = egui::vec2(*x, *y) * line_height; + self.raw.events.push(egui::Event::Scroll(scroll_delta)); } winit::event::MouseScrollDelta::PixelDelta(delta) => { // Actually point delta - self.raw.scroll_delta = egui::vec2(delta.x as f32, delta.y as f32); + let scroll_delta = egui::vec2(delta.x as f32, delta.y as f32); + self.raw.events.push(egui::Event::Scroll(scroll_delta)); } } } @@ -346,6 +347,7 @@ impl Renderer { target_msaa_samples, ), paint_jobs: Vec::new(), + textures_delta: Default::default(), } } @@ -360,7 +362,6 @@ impl Renderer { /// Encode a render pass for drawing the given context's texture to the given `dst_texture`. pub fn encode_render_pass( &mut self, - context: &CtxRef, device: &wgpu::Device, queue: &wgpu::Queue, encoder: &mut wgpu::CommandEncoder, @@ -368,6 +369,7 @@ impl Renderer { dst_scale_factor: f32, dst_texture: &wgpu::TextureView, ) -> Result<(), egui_wgpu_backend::BackendError> { + let textures_delta = std::mem::take(&mut self.textures_delta); let render_pass = &mut self.render_pass; let paint_jobs = &self.paint_jobs; let [physical_width, physical_height] = dst_size_pixels; @@ -376,16 +378,15 @@ impl Renderer { physical_height, scale_factor: dst_scale_factor, }; - render_pass.update_texture(device, queue, &*context.texture()); - render_pass.update_user_textures(&device, &queue); + render_pass.add_textures(device, queue, &textures_delta)?; render_pass.update_buffers(device, queue, &paint_jobs, &screen_descriptor); - render_pass.execute(encoder, dst_texture, &paint_jobs, &screen_descriptor, None) + render_pass.execute(encoder, dst_texture, &paint_jobs, &screen_descriptor, None)?; + render_pass.remove_textures(textures_delta) } /// Encodes a render pass for drawing the given context's texture to the given frame. pub fn draw_to_frame( &mut self, - context: &CtxRef, frame: &nannou::Frame, ) -> Result<(), egui_wgpu_backend::BackendError> { let device_queue_pair = frame.device_queue_pair(); @@ -397,7 +398,6 @@ impl Renderer { let texture_view = frame.texture_view(); let mut encoder = frame.command_encoder(); self.encode_render_pass( - context, device, queue, &mut encoder, @@ -409,8 +409,8 @@ impl Renderer { } impl<'a> FrameCtx<'a> { - /// Produces a `CtxRef` ready for describing the UI for this frame. - pub fn context(&self) -> CtxRef { + /// Produces a `Context` ready for describing the UI for this frame. + pub fn context(&self) -> Context { self.ui.context.clone() } @@ -435,13 +435,13 @@ impl<'a> Drop for FrameCtx<'a> { } impl<'a> Deref for FrameCtx<'a> { - type Target = egui::CtxRef; + type Target = Context; fn deref(&self) -> &Self::Target { &self.ui.context } } -impl epi::RepaintSignal for RepaintSignal { +impl epi::backend::RepaintSignal for RepaintSignal { fn request_repaint(&self) { if let Ok(guard) = self.0.lock() { guard.wakeup().ok(); diff --git a/nannou_egui_demo_app/Cargo.toml b/nannou_egui_demo_app/Cargo.toml index cf12f3769..6f53b72ef 100644 --- a/nannou_egui_demo_app/Cargo.toml +++ b/nannou_egui_demo_app/Cargo.toml @@ -8,4 +8,4 @@ publish = false [dependencies] nannou_egui = { version = "0.5", path = "../nannou_egui" } nannou = { version = "0.18.1", path = "../nannou" } -egui_demo_lib = "0.15" +egui_demo_lib = "0.17" diff --git a/nannou_egui_demo_app/src/main.rs b/nannou_egui_demo_app/src/main.rs index 7c91a4a5c..2d2b8c687 100644 --- a/nannou_egui_demo_app/src/main.rs +++ b/nannou_egui_demo_app/src/main.rs @@ -1,5 +1,5 @@ use nannou::prelude::*; -use nannou_egui::{egui_wgpu_backend::epi::App as EguiApp, Egui}; +use nannou_egui::{epi::App as EguiApp, Egui}; fn main() { nannou::app(model).update(update).run(); From f44cb2af3f766f0d1a99e7fbffc9257c341fe3f1 Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Wed, 18 May 2022 20:13:48 +1000 Subject: [PATCH 5/6] WIP update to egui 0.18 --- nannou_egui/Cargo.toml | 5 +- nannou_egui/src/lib.rs | 198 +++++++++++++++---------------- nannou_egui_demo_app/src/main.rs | 3 + 3 files changed, 104 insertions(+), 102 deletions(-) diff --git a/nannou_egui/Cargo.toml b/nannou_egui/Cargo.toml index 1e8febd27..b4755d675 100644 --- a/nannou_egui/Cargo.toml +++ b/nannou_egui/Cargo.toml @@ -12,8 +12,7 @@ repository = "https://github.com/AlexEne/nannou_egui" readme = "../README.md" [dependencies] -egui_wgpu_backend = "0.17" -egui = "0.17" -epi = "0.17" +egui-wgpu = "0.18" +egui = "0.18" winit = "0.26" nannou = { version = "0.18.1", path = "../nannou" } diff --git a/nannou_egui/src/lib.rs b/nannou_egui/src/lib.rs index ce01afe6d..fae529b93 100644 --- a/nannou_egui/src/lib.rs +++ b/nannou_egui/src/lib.rs @@ -1,10 +1,10 @@ pub use egui; pub use egui::color_picker; -pub use egui_wgpu_backend; -pub use epi; +pub use egui_wgpu; +//pub use epi; -use egui::{pos2, ClippedMesh, Context}; -use egui_wgpu_backend::ScreenDescriptor; +use egui::{pos2, ClippedPrimitive, Context}; +use egui_wgpu::renderer::{RenderPass, ScreenDescriptor}; use nannou::{wgpu, winit::event::VirtualKeyCode, winit::event::WindowEvent::*}; use std::{ cell::RefCell, @@ -29,8 +29,8 @@ pub struct Egui { /// /// For targeting more than one window, users should construct a `Egui` for each. pub struct Renderer { - render_pass: egui_wgpu_backend::RenderPass, - paint_jobs: Vec, + render_pass: RenderPass, + paint_jobs: Vec, textures_delta: egui::TexturesDelta, } @@ -118,85 +118,85 @@ impl Egui { FrameCtx { ui, ended } } - /// Registers a wgpu::Texture with a egui::TextureId. - pub fn texture_from_wgpu_texture( - &mut self, - device: &wgpu::Device, - texture: &wgpu::TextureView, - texture_filter: wgpu::FilterMode, - ) -> egui::TextureId { - self.renderer - .borrow_mut() - .render_pass - .egui_texture_from_wgpu_texture(device, texture, texture_filter) - } - - /// Registers a wgpu::Texture with an existing egui::TextureId. - pub fn update_texture_from_wgpu_texture( - &mut self, - device: &wgpu::Device, - texture: &wgpu::TextureView, - texture_filter: wgpu::FilterMode, - id: egui::TextureId, - ) -> Result<(), egui_wgpu_backend::BackendError> { - self.renderer - .borrow_mut() - .render_pass - .update_egui_texture_from_wgpu_texture(device, texture, texture_filter, id) - } + // /// Registers a wgpu::Texture with a egui::TextureId. + // pub fn texture_from_wgpu_texture( + // &mut self, + // device: &wgpu::Device, + // texture: &wgpu::TextureView, + // texture_filter: wgpu::FilterMode, + // ) -> egui::TextureId { + // self.renderer + // .borrow_mut() + // .render_pass + // .egui_texture_from_wgpu_texture(device, texture, texture_filter) + // } + + // /// Registers a wgpu::Texture with an existing egui::TextureId. + // pub fn update_texture_from_wgpu_texture( + // &mut self, + // device: &wgpu::Device, + // texture: &wgpu::TextureView, + // texture_filter: wgpu::FilterMode, + // id: egui::TextureId, + // ) { + // self.renderer + // .borrow_mut() + // .render_pass + // .update_egui_texture_from_wgpu_texture(device, texture, texture_filter, id) + // } /// Draws the contents of the inner `context` to the given frame. pub fn draw_to_frame( &self, frame: &nannou::Frame, - ) -> Result<(), egui_wgpu_backend::BackendError> { + ) { let mut renderer = self.renderer.borrow_mut(); renderer.draw_to_frame(frame) } - /// Provide access to an `epi::Frame` within the given function. - /// - /// This method is primarily used for apps based on the `epi` interface. - pub fn with_epi_frame(&mut self, proxy: nannou::app::Proxy, f: F) - where - F: FnOnce(&Context, &mut epi::Frame), - { - let integration_info = epi::IntegrationInfo { - native_pixels_per_point: Some(self.input.window_scale_factor as _), - // TODO: Provide access to this stuff. - web_info: None, - prefer_dark_mode: None, - cpu_usage: None, - name: "egui_nannou_wgpu", - }; - let app_output = epi::backend::AppOutput::default(); - let repaint_signal = Arc::new(RepaintSignal(Mutex::new(proxy))); - let frame_data = epi::backend::FrameData { - info: integration_info, - output: app_output, - repaint_signal: repaint_signal as Arc<_>, - }; - let mut frame = epi::Frame(Arc::new(Mutex::new(frame_data))); - f(&self.context, &mut frame) - } - - /// The same as `with_epi_frame`, but calls `begin_frame` before calling the given function, - /// and then calls `end_frame` before returning. - pub fn do_frame_with_epi_frame(&mut self, proxy: nannou::app::Proxy, f: F) -> egui::FullOutput - where - F: FnOnce(&Context, &mut epi::Frame), - { - self.begin_frame_inner(); - self.with_epi_frame(proxy.clone(), f); - let output = self.end_frame_inner(); - - // If a repaint is required, ensure the event loop emits another update. - if output.needs_repaint { - proxy.wakeup().unwrap(); - } - - output - } + // /// Provide access to an `epi::Frame` within the given function. + // /// + // /// This method is primarily used for apps based on the `epi` interface. + // pub fn with_epi_frame(&mut self, proxy: nannou::app::Proxy, f: F) + // where + // F: FnOnce(&Context, &mut epi::Frame), + // { + // let integration_info = epi::IntegrationInfo { + // native_pixels_per_point: Some(self.input.window_scale_factor as _), + // // TODO: Provide access to this stuff. + // web_info: None, + // prefer_dark_mode: None, + // cpu_usage: None, + // name: "egui_nannou_wgpu", + // }; + // let app_output = epi::backend::AppOutput::default(); + // let repaint_signal = Arc::new(RepaintSignal(Mutex::new(proxy))); + // let frame_data = epi::backend::FrameData { + // info: integration_info, + // output: app_output, + // repaint_signal: repaint_signal as Arc<_>, + // }; + // let mut frame = epi::Frame(Arc::new(Mutex::new(frame_data))); + // f(&self.context, &mut frame) + // } + + // /// The same as `with_epi_frame`, but calls `begin_frame` before calling the given function, + // /// and then calls `end_frame` before returning. + // pub fn do_frame_with_epi_frame(&mut self, proxy: nannou::app::Proxy, f: F) -> egui::FullOutput + // where + // F: FnOnce(&Context, &mut epi::Frame), + // { + // self.begin_frame_inner(); + // self.with_epi_frame(proxy.clone(), f); + // let output = self.end_frame_inner(); + + // // If a repaint is required, ensure the event loop emits another update. + // if output.needs_repaint { + // proxy.wakeup().unwrap(); + // } + + // output + // } fn begin_frame_inner(&mut self) { self.context.begin_frame(self.input.raw.take()); @@ -340,12 +340,9 @@ impl Renderer { target_format: wgpu::TextureFormat, target_msaa_samples: u32, ) -> Self { + let render_pass = RenderPass::new(device, target_format, target_msaa_samples); Self { - render_pass: egui_wgpu_backend::RenderPass::new( - device, - target_format, - target_msaa_samples, - ), + render_pass, paint_jobs: Vec::new(), textures_delta: Default::default(), } @@ -368,27 +365,30 @@ impl Renderer { dst_size_pixels: [u32; 2], dst_scale_factor: f32, dst_texture: &wgpu::TextureView, - ) -> Result<(), egui_wgpu_backend::BackendError> { + ) { let textures_delta = std::mem::take(&mut self.textures_delta); let render_pass = &mut self.render_pass; let paint_jobs = &self.paint_jobs; - let [physical_width, physical_height] = dst_size_pixels; - let screen_descriptor = ScreenDescriptor { - physical_width, - physical_height, - scale_factor: dst_scale_factor, - }; - render_pass.add_textures(device, queue, &textures_delta)?; + let size_in_pixels = dst_size_pixels; + let pixels_per_point = dst_scale_factor; + let screen_descriptor = ScreenDescriptor { size_in_pixels, pixels_per_point }; + for (id, image_delta) in &textures_delta.set { + render_pass.update_texture(device, queue, *id, image_delta); + } + for id in &textures_delta.free { + render_pass.free_texture(id); + } render_pass.update_buffers(device, queue, &paint_jobs, &screen_descriptor); - render_pass.execute(encoder, dst_texture, &paint_jobs, &screen_descriptor, None)?; - render_pass.remove_textures(textures_delta) + render_pass.execute(encoder, dst_texture, &paint_jobs, &screen_descriptor, None); + + //render_pass.remove_textures(textures_delta) } /// Encodes a render pass for drawing the given context's texture to the given frame. pub fn draw_to_frame( &mut self, frame: &nannou::Frame, - ) -> Result<(), egui_wgpu_backend::BackendError> { + ) { let device_queue_pair = frame.device_queue_pair(); let device = device_queue_pair.device(); let queue = device_queue_pair.queue(); @@ -441,13 +441,13 @@ impl<'a> Deref for FrameCtx<'a> { } } -impl epi::backend::RepaintSignal for RepaintSignal { - fn request_repaint(&self) { - if let Ok(guard) = self.0.lock() { - guard.wakeup().ok(); - } - } -} +// impl epi::backend::RepaintSignal for RepaintSignal { +// fn request_repaint(&self) { +// if let Ok(guard) = self.0.lock() { +// guard.wakeup().ok(); +// } +// } +// } /// Translates winit to egui keycodes. #[inline] diff --git a/nannou_egui_demo_app/src/main.rs b/nannou_egui_demo_app/src/main.rs index 2d2b8c687..898582716 100644 --- a/nannou_egui_demo_app/src/main.rs +++ b/nannou_egui_demo_app/src/main.rs @@ -11,6 +11,7 @@ struct Model { } fn model(app: &App) -> Model { + println!("model"); app.set_loop_mode(LoopMode::wait()); let w_id = app .new_window() @@ -36,6 +37,7 @@ fn raw_window_event(_app: &App, model: &mut Model, event: &nannou::winit::event: } fn update(app: &App, model: &mut Model, update: Update) { + println!("update"); let Model { ref mut egui, ref mut egui_demo_app, @@ -49,5 +51,6 @@ fn update(app: &App, model: &mut Model, update: Update) { } fn view(_app: &App, model: &Model, frame: Frame) { + println!("view"); model.egui.draw_to_frame(&frame).unwrap(); } From cd2a890ee245f8e167af826fbfe958fa4299b5ee Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Sun, 29 May 2022 16:13:18 +1000 Subject: [PATCH 6/6] Remove `unwrap` following egui draw_to_frame calls in UI examples In the migration of `egui_wgpu_backend` into the main repositories, it looks like results are no longer returned, so there's no error handling to be done here. --- examples/ui/egui/circle_packing.rs | 2 +- examples/ui/egui/tune_color.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/ui/egui/circle_packing.rs b/examples/ui/egui/circle_packing.rs index 3a9c0f6d1..b0a206d21 100644 --- a/examples/ui/egui/circle_packing.rs +++ b/examples/ui/egui/circle_packing.rs @@ -96,7 +96,7 @@ fn view(app: &App, model: &Model, frame: Frame) { draw.to_frame(app, &frame).unwrap(); - model.egui.draw_to_frame(&frame).unwrap(); + model.egui.draw_to_frame(&frame); } fn intersects(circle: &Circle, circles: &Vec) -> bool { diff --git a/examples/ui/egui/tune_color.rs b/examples/ui/egui/tune_color.rs index 6e7b9e4b0..865a5f6b6 100644 --- a/examples/ui/egui/tune_color.rs +++ b/examples/ui/egui/tune_color.rs @@ -71,7 +71,7 @@ fn view(app: &App, model: &Model, frame: Frame) { draw.to_frame(app, &frame).unwrap(); // Do this as the last operation on your frame. - model.egui.draw_to_frame(&frame).unwrap(); + model.egui.draw_to_frame(&frame); } fn edit_hsv(ui: &mut egui::Ui, color: &mut Hsv) {