From 9cf59df86744db4f7942d6d572a22da5a0877f3b Mon Sep 17 00:00:00 2001 From: Filipe Rodrigues Date: Wed, 17 Sep 2025 18:30:16 +0100 Subject: [PATCH] Fade shader now renders the 3 images separately. --- zsw/shaders/panels/fade.wgsl | 78 +++++++- zsw/shaders/panels/fade/frag.wgsl | 112 ----------- zsw/shaders/panels/fade/stage_io.wgsl | 25 --- zsw/shaders/panels/fade/uniforms.wgsl | 30 --- zsw/shaders/panels/fade/vertex.wgsl | 15 -- .../menu/metrics/frame_times/render_panels.rs | 88 ++++++--- zsw/src/metrics.rs | 7 + zsw/src/panel/geometry.rs | 18 +- zsw/src/panel/renderer.rs | 184 ++++++++++-------- zsw/src/panel/renderer/uniform.rs | 78 +++----- zsw/src/panel/state/fade.rs | 2 +- zsw/src/panel/state/fade/images.rs | 112 +++++------ 12 files changed, 327 insertions(+), 422 deletions(-) delete mode 100644 zsw/shaders/panels/fade/frag.wgsl delete mode 100644 zsw/shaders/panels/fade/stage_io.wgsl delete mode 100644 zsw/shaders/panels/fade/uniforms.wgsl delete mode 100644 zsw/shaders/panels/fade/vertex.wgsl diff --git a/zsw/shaders/panels/fade.wgsl b/zsw/shaders/panels/fade.wgsl index 8630242..201659c 100644 --- a/zsw/shaders/panels/fade.wgsl +++ b/zsw/shaders/panels/fade.wgsl @@ -1,17 +1,77 @@ -//! Fade shader +//! None shader -// Imports -#import fade::vertex -#import fade::frag -#import fade::stage_io::{VertexInput, VertexOutput, FragOutput} +/// Uniforms +struct Uniforms { + pos_matrix: mat4x4, + image_ratio: vec2, + progress: f32, + alpha: f32, + #ifdef FADE_WHITE + mix_strength: f32, + #else ifdef FADE_OUT + strength: f32, + #else ifdef FADE_IN + strength: f32, + #endif +}; + +/// Uniforms +@group(0) @binding(0) +var uniforms: Uniforms; + +/// Vertex output +struct VertexOutput { + @builtin(position) + pos: vec4, + + @location(0) + uvs: vec2, +}; @vertex -fn vs_main(in: VertexInput) -> VertexOutput { - return vertex::main(in); +fn vs_main( + @location(0) pos: vec2, + @location(1) uvs: vec2, +) -> VertexOutput { + var out: VertexOutput; + out.pos = uniforms.pos_matrix * vec4(pos, 0.0, 1.0); + out.uvs = uvs; + return out; } +// Image +@group(1) @binding(0) var image: texture_2d; +@group(1) @binding(1) var image_sampler: sampler; + @fragment -fn fs_main(in: VertexOutput) -> FragOutput { - return frag::main(in); +fn fs_main(in: VertexOutput) -> @location(0) vec4 { + // Calculate the uvs for this pixel + let uvs_offset = (vec2(1.0, 1.0) - uniforms.image_ratio) * uniforms.progress; + var uvs = in.uvs * uniforms.image_ratio + uvs_offset; + + #ifdef FADE_OUT + let mid = uniforms.image_ratio / 2.0 + uvs_offset; + uvs = mid + (uvs - mid) * pow(uniforms.alpha, uniforms.strength); + #else ifdef FADE_IN + let mid = uniforms.image_ratio / 2.0 + uvs_offset; + uvs = mid + (uvs - mid) / pow(uniforms.alpha, uniforms.strength); + #endif + + // If we'd sample outside the image, discard this pixel instead + // TODO: Set alpha to 0 instead of discarding? + if any(uvs < vec2(0.0, 0.0) | uvs > vec2(1.0, 1.0)) { + discard; + } + + // Otherwise, we'll sample and return the color + var color = textureSample(image, image_sampler, uvs); + #ifdef FADE_WHITE + color = mix( + color, + vec4(1.0, 1.0, 1.0, 1.0), + uniforms.mix_strength, + ); + #endif + return vec4(color.rgb, uniforms.alpha); } diff --git a/zsw/shaders/panels/fade/frag.wgsl b/zsw/shaders/panels/fade/frag.wgsl deleted file mode 100644 index 894351e..0000000 --- a/zsw/shaders/panels/fade/frag.wgsl +++ /dev/null @@ -1,112 +0,0 @@ -//! Frag shader - -// Imports -#import fade::stage_io::{VertexOutput, FragOutput} -#import fade::uniforms::{uniforms, ImageUniforms} - -// Bindings -@group(1) @binding(0) var image_prev: texture_2d; -@group(1) @binding(1) var image_cur: texture_2d; -@group(1) @binding(2) var image_next: texture_2d; -@group(1) @binding(3) var image_sampler: sampler; - -struct Sampled { - color: vec4, - uvs : vec2, -} - -// Samples an image -fn sample(image: texture_2d, in_uvs: vec2, image_uniforms: ImageUniforms, progress_raw: f32, alpha: f32) -> Sampled { - var sampled: Sampled; - var uvs = in_uvs; - - var progress: f32; - if image_uniforms.swap_dir == 0 { - progress = progress_raw; - } else { - progress = 1.0 - progress_raw; - } - - // Then apply the image ratio and delta - let uvs_delta = (vec2(1.0, 1.0) - image_uniforms.image_ratio) * progress; - uvs = uvs * image_uniforms.image_ratio + uvs_delta; - - // Offset it, if necessary - #ifdef FADE_OUT - let mid = vec2(image_uniforms.image_ratio.x / 2.0 + uvs_delta.x, image_uniforms.image_ratio.y / 2.0 + uvs_delta.y); - uvs = (uvs.xy - mid) * pow(alpha, uniforms.strength) + mid; - - #else ifdef FADE_IN - let mid = vec2(image_uniforms.image_ratio.x / 2.0 + uvs_delta.x, image_uniforms.image_ratio.y / 2.0 + uvs_delta.y); - uvs = (uvs.xy - mid) / pow(alpha, uniforms.strength) + mid; - - #endif - - sampled.color = textureSample(image, image_sampler, uvs); - sampled.uvs = uvs; - - return sampled; -} - -fn main(in: VertexOutput) -> FragOutput { - var out: FragOutput; - - let p = uniforms.progress; - let f = uniforms.fade_duration; - - // Full duration an image is on screen (including the fades) - let d = 1.0 + 2.0 * f; - - let progress_prev = 1.0 - max((f - p) / d, 0.0); - let progress_cur = (p + f) / d; - let progress_next = max((p - 1.0 + f) / d, 0.0); - - let alpha_prev = 0.5 * saturate(1.0 - ( p) / f); - let alpha_next = 0.5 * saturate(1.0 - (1.0 - p) / f); - let alpha_cur = 1.0 - max(alpha_prev, alpha_next); - - // Sample the images - let sample_prev = sample(image_prev, in.uvs, uniforms.prev, progress_prev, alpha_prev); - let sample_cur = sample( image_cur, in.uvs, uniforms.cur , progress_cur , alpha_cur ); - let sample_next = sample(image_next, in.uvs, uniforms.next, progress_next, alpha_next); - - // Then mix the color - // TODO: Don't repeat this once we're able to use `defined(FADE_BASIC) || defined(FADE_OUT)` - #ifdef FADE_BASIC - out.color = - alpha_prev * sample_prev.color + - alpha_cur * sample_cur .color + - alpha_next * sample_next.color; - out.color.a = 1.0; - #else ifdef FADE_OUT - out.color = - alpha_prev * sample_prev.color + - alpha_cur * sample_cur .color + - alpha_next * sample_next.color; - out.color.a = 1.0; - - #else ifdef FADE_WHITE - out.color = - alpha_prev * sample_prev.color + - alpha_cur * sample_cur .color + - alpha_next * sample_next.color; - out.color = mix( - out.color, - vec4(1.0, 1.0, 1.0, 1.0), - uniforms.strength * max(4.0 * alpha_cur * alpha_prev, 4.0 * alpha_cur * alpha_next) - ); - out.color.a = 1.0; - - #else ifdef FADE_IN - let contained_prev = sample_prev.uvs.x >= 0.0 && sample_prev.uvs.x <= 1.0 && sample_prev.uvs.y >= 0.0 && sample_prev.uvs.y <= 1.0; - let contained_cur = sample_cur .uvs.x >= 0.0 && sample_cur .uvs.x <= 1.0 && sample_cur .uvs.y >= 0.0 && sample_cur .uvs.y <= 1.0; - let contained_next = sample_next.uvs.x >= 0.0 && sample_next.uvs.x <= 1.0 && sample_next.uvs.y >= 0.0 && sample_next.uvs.y <= 1.0; - out.color = - alpha_prev * sample_prev.color * f32(contained_prev) + - alpha_cur * sample_cur .color * f32(contained_cur ) + - alpha_next * sample_next.color * f32(contained_next) ; - out.color.a = f32(contained_prev || contained_cur || contained_next); - #endif - - return out; -} diff --git a/zsw/shaders/panels/fade/stage_io.wgsl b/zsw/shaders/panels/fade/stage_io.wgsl deleted file mode 100644 index 132dc37..0000000 --- a/zsw/shaders/panels/fade/stage_io.wgsl +++ /dev/null @@ -1,25 +0,0 @@ -//! Stage Input/Output - -// Vertex input -struct VertexInput { - @location(0) - pos: vec2, - - @location(1) - uvs: vec2, -}; - -// Vertex output / Frag Input -struct VertexOutput { - @builtin(position) - pos: vec4, - - @location(0) - uvs: vec2, -}; - -// Frag output -struct FragOutput { - @location(0) - color: vec4, -}; diff --git a/zsw/shaders/panels/fade/uniforms.wgsl b/zsw/shaders/panels/fade/uniforms.wgsl deleted file mode 100644 index eae1084..0000000 --- a/zsw/shaders/panels/fade/uniforms.wgsl +++ /dev/null @@ -1,30 +0,0 @@ -//! Uniforms - -/// Uniforms for each image -struct ImageUniforms { - image_ratio: vec2, - swap_dir: u32, -} - -/// Uniforms -struct Uniforms { - pos_matrix: mat4x4, - prev: ImageUniforms, - cur: ImageUniforms, - next: ImageUniforms, - fade_duration: f32, - progress: f32, - - // TODO: Reduce this repetition - #ifdef FADE_WHITE - strength: f32, - #else ifdef FADE_OUT - strength: f32, - #else ifdef FADE_IN - strength: f32, - #endif -}; - -// Uniforms -@group(0) @binding(0) -var uniforms: Uniforms; diff --git a/zsw/shaders/panels/fade/vertex.wgsl b/zsw/shaders/panels/fade/vertex.wgsl deleted file mode 100644 index d937a73..0000000 --- a/zsw/shaders/panels/fade/vertex.wgsl +++ /dev/null @@ -1,15 +0,0 @@ -//! Vertex shader - -// Imports -#import fade::stage_io::{VertexInput, VertexOutput} -#import fade::uniforms::uniforms - -// Vertex entry -fn main(in: VertexInput) -> VertexOutput { - var out: VertexOutput; - - out.pos = uniforms.pos_matrix * vec4(in.pos, 0.0, 1.0); - out.uvs = in.uvs; - - return out; -} diff --git a/zsw/src/menu/metrics/frame_times/render_panels.rs b/zsw/src/menu/metrics/frame_times/render_panels.rs index c1a8905..5a215b6 100644 --- a/zsw/src/menu/metrics/frame_times/render_panels.rs +++ b/zsw/src/menu/metrics/frame_times/render_panels.rs @@ -2,14 +2,17 @@ // Imports use { - crate::metrics::{ - FrameTimes, - RenderPanelFrameTime, - RenderPanelGeometryFadeFrameTime, - RenderPanelGeometryFrameTime, - RenderPanelGeometryNoneFrameTime, - RenderPanelGeometrySlideFrameTime, - RenderPanelsFrameTime, + crate::{ + metrics::{ + FrameTimes, + RenderPanelFrameTime, + RenderPanelGeometryFadeImageFrameTime, + RenderPanelGeometryFrameTime, + RenderPanelGeometryNoneFrameTime, + RenderPanelGeometrySlideFrameTime, + RenderPanelsFrameTime, + }, + panel::state::fade::PanelFadeImageSlot, }, core::{iter, time::Duration}, itertools::Itertools, @@ -27,7 +30,10 @@ pub fn draw(ui: &mut egui::Ui, render_frame_times: &mut FrameTimes, } struct DurationPanelIdxTree { - geometries: HashSet, + geometries: HashMap, + } + struct DurationPanelGeometryIdxTree { + images: HashSet, } let duration_idxs_tree = DurationIdxTree { panels: render_frame_times @@ -35,7 +41,23 @@ pub fn draw(ui: &mut egui::Ui, render_frame_times: &mut FrameTimes HashSet::new(), + RenderPanelGeometryFrameTime::Fade(images) => + images.images.iter().map(|(&image_slot, _image)| image_slot).collect(), + RenderPanelGeometryFrameTime::Slide(_) => HashSet::new(), + }, + }; + (geometry_idx, geometry) + }) + .collect(), }; (panel_idx, panel) }) @@ -51,7 +73,7 @@ pub fn draw(ui: &mut egui::Ui, render_frame_times: &mut FrameTimes String { match self { Self::None { inner } => inner.name(panel_idx, geometry_idx), - Self::Fade { inner } => inner.name(panel_idx, geometry_idx), + Self::Fade { image_slot, inner } => inner.name(panel_idx, geometry_idx, image_slot), Self::Slide { inner } => inner.name(panel_idx, geometry_idx), } } @@ -169,7 +200,10 @@ impl DurationPanelGeometryIdx { pub fn duration(self, frame_time: &RenderPanelGeometryFrameTime) -> Option { match (self, frame_time) { (Self::None { inner }, RenderPanelGeometryFrameTime::None(frame_time)) => inner.duration(frame_time), - (Self::Fade { inner }, RenderPanelGeometryFrameTime::Fade(frame_time)) => inner.duration(frame_time), + (Self::Fade { image_slot, inner }, RenderPanelGeometryFrameTime::Fade(frame_time)) => frame_time + .images + .get(&image_slot) + .and_then(|image| inner.duration(image)), (Self::Slide { inner }, RenderPanelGeometryFrameTime::Slide(frame_time)) => inner.duration(frame_time), _ => None, } @@ -206,15 +240,17 @@ enum DurationPanelGeometryFadeIdx { } impl DurationPanelGeometryFadeIdx { - pub fn name(self, panel_idx: usize, geometry_idx: usize) -> String { + pub fn name(self, panel_idx: usize, geometry_idx: usize, image_slot: PanelFadeImageSlot) -> String { match self { - Self::WriteUniforms => - format!("[Panel${panel_idx}] [Geometry${geometry_idx}] [Shader$Fade] Write uniforms"), - Self::Draw => format!("[Panel${panel_idx}] [Geometry${geometry_idx}] [Shader$Fade] Draw"), + Self::WriteUniforms => format!( + "[Panel${panel_idx}] [Geometry${geometry_idx}] [Shader$Fade] [Image${image_slot:?}] Write uniforms" + ), + Self::Draw => + format!("[Panel${panel_idx}] [Geometry${geometry_idx}] [Shader$Fade] [Image${image_slot:?}] Draw"), } } - pub fn duration(self, frame_time: &RenderPanelGeometryFadeFrameTime) -> Option { + pub fn duration(self, frame_time: &RenderPanelGeometryFadeImageFrameTime) -> Option { match self { Self::WriteUniforms => Some(frame_time.write_uniforms), Self::Draw => Some(frame_time.draw), diff --git a/zsw/src/metrics.rs b/zsw/src/metrics.rs index 8026a06..a8d7055 100644 --- a/zsw/src/metrics.rs +++ b/zsw/src/metrics.rs @@ -2,6 +2,7 @@ // Imports use { + crate::panel::state::fade::PanelFadeImageSlot, core::{ops::DerefMut, time::Duration}, std::collections::{HashMap, VecDeque}, tokio::sync::{Mutex, MutexGuard}, @@ -174,6 +175,12 @@ pub struct RenderPanelGeometryNoneFrameTime { /// Render panel geometry fade frame time. #[derive(Clone, Debug)] pub struct RenderPanelGeometryFadeFrameTime { + pub images: HashMap, +} + +/// Render panel geometry fade image frame time. +#[derive(Clone, Debug)] +pub struct RenderPanelGeometryFadeImageFrameTime { pub write_uniforms: Duration, pub draw: Duration, } diff --git a/zsw/src/panel/geometry.rs b/zsw/src/panel/geometry.rs index a38e794..0cb36b5 100644 --- a/zsw/src/panel/geometry.rs +++ b/zsw/src/panel/geometry.rs @@ -1,7 +1,12 @@ //! Panel geometry // Imports -use {std::collections::HashMap, tokio::sync::OnceCell, winit::window::WindowId}; +use { + crate::panel::state::fade::PanelFadeImageSlot, + std::collections::HashMap, + tokio::sync::OnceCell, + winit::window::WindowId, +}; /// Panel geometry #[derive(Debug)] @@ -14,7 +19,7 @@ pub struct PanelGeometry { #[derive(Default, Debug)] pub struct PanelGeometryUniforms { pub none: OnceCell, - pub fade: OnceCell, + pub fade: PanelGeometryFadeUniforms, pub slide: OnceCell, } @@ -29,8 +34,15 @@ pub struct PanelGeometryNoneUniforms { } /// Panel geometry fade uniforms -#[derive(Debug)] +#[derive(Default, Debug)] pub struct PanelGeometryFadeUniforms { + /// Images + pub images: HashMap, +} + +/// Panel geometry fade image uniforms +#[derive(Debug)] +pub struct PanelGeometryFadeImageUniforms { /// Buffer pub buffer: wgpu::Buffer, diff --git a/zsw/src/panel/renderer.rs b/zsw/src/panel/renderer.rs index a545188..588f2a1 100644 --- a/zsw/src/panel/renderer.rs +++ b/zsw/src/panel/renderer.rs @@ -9,17 +9,16 @@ pub use self::{uniform::MAX_UNIFORM_SIZE, vertex::PanelVertex}; // Imports use { - self::uniform::PanelFadeImageUniforms, super::{ PanelState, Panels, - geometry::{PanelGeometryFadeUniforms, PanelGeometryNoneUniforms, PanelGeometrySlideUniforms}, - state::fade::PanelFadeImagesShared, + geometry::{PanelGeometryFadeImageUniforms, PanelGeometryNoneUniforms, PanelGeometrySlideUniforms}, + state::fade::{PanelFadeImagesShared, images::PanelFadeImageSlot}, }, crate::{ display::DisplayGeometry, metrics::{self, Metrics}, - panel::{PanelGeometry, state::fade::PanelFadeImage}, + panel::PanelGeometry, time, }, app_error::Context, @@ -51,6 +50,7 @@ pub struct PanelsRendererShared { /// Index buffer indices: wgpu::Buffer, + // TODO: Move these to the respective shader shared types /// Uniforms none bind group layout none_uniforms_bind_group_layout: wgpu::BindGroupLayout, @@ -357,91 +357,111 @@ impl PanelsRenderer { }) }, PanelState::Fade(panel_state) => { - let geometry_uniforms = geometry_uniforms - .fade - .get_or_init(async || { - self::create_geometry_fade_uniforms(wgpu, &shared.fade_uniforms_bind_group_layout) - }) - .await; + let p = panel_state.progress_norm(); + let f = panel_state.fade_duration_norm(); - let image_uniforms = |image: Option<&PanelFadeImage>| { - let (size, swap_dir) = match image { - None => (Vector2::new(0, 0), false), - Some(image) => { - let texture = image.texture_view.texture(); - let size = Vector2::new(texture.width(), texture.height()); - (size, image.swap_dir) - }, + // Full duration an image is on screen (including the fades) + let d = 1.0 + 2.0 * f; + + let mut image_metrics = HashMap::new(); + for (panel_image_slot, panel_image) in panel_state.images().iter() { + let geometry_uniforms = + geometry_uniforms + .fade + .images + .entry(panel_image_slot) + .or_insert_with(|| { + self::create_geometry_fade_image_uniforms(wgpu, &shared.fade_uniforms_bind_group_layout) + }); + + let progress = match panel_image_slot { + PanelFadeImageSlot::Prev => 1.0 - f32::max((f - p) / d, 0.0), + PanelFadeImageSlot::Cur => (p + f) / d, + PanelFadeImageSlot::Next => f32::max((p - 1.0 + f) / d, 0.0), + }; + let progress = match panel_image.swap_dir { + true => 1.0 - progress, + false => progress, }; - let ratio = display_geometry.image_ratio(size); - PanelFadeImageUniforms::new(ratio, swap_dir) - }; + let alpha_prev = 0.5 * f32::clamp(1.0 - p / f, 0.0, 1.0); + let alpha_next = 0.5 * f32::clamp(1.0 - (1.0 - p) / f, 0.0, 1.0); + let alpha_cur = 1.0 - f32::max(alpha_prev, alpha_next); + let alpha = match panel_image_slot { + PanelFadeImageSlot::Prev => alpha_prev, + PanelFadeImageSlot::Cur => alpha_cur, + PanelFadeImageSlot::Next => alpha_next, + }; - let prev = image_uniforms(panel_state.images().prev.as_ref()); - let cur = image_uniforms(panel_state.images().cur.as_ref()); - let next = image_uniforms(panel_state.images().next.as_ref()); + // If the alpha is 0, we can skip this image + // TODO: If alpha of a previous layer is 1, should we also skip it? + // Right now that's fine, since with alpha 1, we always cover the + // whole screen, but that's not guaranteed in the future. + if alpha == 0.0 { + continue; + } - let fade_duration = panel_state.fade_duration_norm(); - let progress = panel_state.progress_norm(); - #[time(write_uniforms)] - let () = match panel_state.shader() { - PanelFadeShader::Basic => Self::write_uniforms(wgpu, &geometry_uniforms.buffer, uniform::Fade { - pos_matrix, - prev, - cur, - next, - fade_duration, - progress, - _unused: [0; 2], - }), - PanelFadeShader::White { strength } => - Self::write_uniforms(wgpu, &geometry_uniforms.buffer, uniform::FadeWhite { - pos_matrix, - prev, - cur, - next, - fade_duration, - progress, - strength, - _unused: 0, - }), - PanelFadeShader::Out { strength } => - Self::write_uniforms(wgpu, &geometry_uniforms.buffer, uniform::FadeOut { - pos_matrix, - prev, - cur, - next, - fade_duration, - progress, - strength, - _unused: 0, - }), - PanelFadeShader::In { strength } => - Self::write_uniforms(wgpu, &geometry_uniforms.buffer, uniform::FadeIn { - pos_matrix, - prev, - cur, - next, - fade_duration, - progress, - strength, - _unused: 0, - }), - }; + // Calculate the position matrix for the panel + let image_size = panel_image.texture_view.texture().size(); + let image_size = Vector2::new(image_size.width, image_size.height); + let image_ratio = display_geometry.image_ratio(image_size); - // Bind the geometry uniforms - render_pass.set_bind_group(0, &geometry_uniforms.bind_group, &[]); + #[time(write_uniforms)] + let () = match panel_state.shader() { + PanelFadeShader::Basic => + Self::write_uniforms(wgpu, &geometry_uniforms.buffer, uniform::FadeBasic { + pos_matrix, + image_ratio: uniform::Vec2(image_ratio.into()), + progress, + alpha, + }), + PanelFadeShader::White { strength } => + Self::write_uniforms(wgpu, &geometry_uniforms.buffer, uniform::FadeWhite { + pos_matrix, + image_ratio: uniform::Vec2(image_ratio.into()), + progress, + alpha, + mix_strength: strength * 4.0 * f32::max(alpha_cur * alpha_prev, alpha_cur * alpha_next), + _unused: [0; _], + }), + PanelFadeShader::Out { strength } => + Self::write_uniforms(wgpu, &geometry_uniforms.buffer, uniform::FadeOut { + pos_matrix, + image_ratio: uniform::Vec2(image_ratio.into()), + progress, + alpha, + strength, + _unused: [0; _], + }), + PanelFadeShader::In { strength } => + Self::write_uniforms(wgpu, &geometry_uniforms.buffer, uniform::FadeIn { + pos_matrix, + image_ratio: uniform::Vec2(image_ratio.into()), + progress, + alpha, + strength, + _unused: [0; _], + }), + }; - // Bind the image uniforms - render_pass.set_bind_group(1, panel_state.images().image_bind_group(wgpu, &shared.fade).await, &[]); + // Bind the geometry uniforms + render_pass.set_bind_group(0, &geometry_uniforms.bind_group, &[]); - #[time(draw)] - render_pass.draw_indexed(0..6, 0, 0..1); + // Bind the image uniforms + let sampler = panel_state.images().image_sampler(wgpu).await; + render_pass.set_bind_group(1, panel_image.bind_group(wgpu, sampler, &shared.fade).await, &[]); + + #[time(draw)] + render_pass.draw_indexed(0..6, 0, 0..1); + + _ = image_metrics.insert(panel_image_slot, metrics::RenderPanelGeometryFadeImageFrameTime { + write_uniforms, + draw, + }); + } metrics::RenderPanelGeometryFrameTime::Fade(metrics::RenderPanelGeometryFadeFrameTime { - write_uniforms, - draw, + images: image_metrics, }) }, PanelState::Slide(_panel_state) => { @@ -503,8 +523,8 @@ fn create_geometry_none_uniforms(wgpu: &Wgpu, layout: &wgpu::BindGroupLayout) -> PanelGeometryNoneUniforms { buffer, bind_group } } -/// Creates the panel fade geometry uniforms -fn create_geometry_fade_uniforms(wgpu: &Wgpu, layout: &wgpu::BindGroupLayout) -> PanelGeometryFadeUniforms { +/// Creates the panel fade image geometry uniforms +fn create_geometry_fade_image_uniforms(wgpu: &Wgpu, layout: &wgpu::BindGroupLayout) -> PanelGeometryFadeImageUniforms { // Create the uniforms let buffer_descriptor = wgpu::BufferDescriptor { label: Some("zsw-panel-fade-geometry-uniforms-buffer"), @@ -525,7 +545,7 @@ fn create_geometry_fade_uniforms(wgpu: &Wgpu, layout: &wgpu::BindGroupLayout) -> }; let bind_group = wgpu.device.create_bind_group(&bind_group_descriptor); - PanelGeometryFadeUniforms { buffer, bind_group } + PanelGeometryFadeImageUniforms { buffer, bind_group } } /// Creates the panel slide geometry uniforms diff --git a/zsw/src/panel/renderer/uniform.rs b/zsw/src/panel/renderer/uniform.rs index f0561ff..bc41d66 100644 --- a/zsw/src/panel/renderer/uniform.rs +++ b/zsw/src/panel/renderer/uniform.rs @@ -21,26 +21,6 @@ pub struct Vec4(pub [f32; 4]); #[repr(C, align(16))] pub struct Matrix4x4(pub [[f32; 4]; 4]); -/// Panel fade image uniforms -#[derive(PartialEq, Clone, Copy, Default, Debug)] -#[derive(Zeroable, Pod)] -#[repr(C)] -pub struct PanelFadeImageUniforms { - ratio: Vec2, - swap_dir: u32, - _unused: u32, -} - -impl PanelFadeImageUniforms { - pub fn new(ratio: impl Into<[f32; 2]>, swap_dir: bool) -> Self { - Self { - ratio: Vec2(ratio.into()), - swap_dir: swap_dir.into(), - _unused: 0, - } - } -} - /// None #[derive(PartialEq, Clone, Copy, Default, Debug)] #[derive(Zeroable, Pod)] @@ -54,15 +34,11 @@ pub struct None { #[derive(PartialEq, Clone, Copy, Default, Debug)] #[derive(Zeroable, Pod)] #[repr(C)] -pub struct Fade { - pub pos_matrix: Matrix4x4, - pub prev: PanelFadeImageUniforms, - pub cur: PanelFadeImageUniforms, - pub next: PanelFadeImageUniforms, - pub fade_duration: f32, - pub progress: f32, - - pub _unused: [u32; 2], +pub struct FadeBasic { + pub pos_matrix: Matrix4x4, + pub image_ratio: Vec2, + pub progress: f32, + pub alpha: f32, } /// Fade-white @@ -70,15 +46,13 @@ pub struct Fade { #[derive(Zeroable, Pod)] #[repr(C)] pub struct FadeWhite { - pub pos_matrix: Matrix4x4, - pub prev: PanelFadeImageUniforms, - pub cur: PanelFadeImageUniforms, - pub next: PanelFadeImageUniforms, - pub fade_duration: f32, - pub progress: f32, - pub strength: f32, + pub pos_matrix: Matrix4x4, + pub image_ratio: Vec2, + pub progress: f32, + pub alpha: f32, + pub mix_strength: f32, - pub _unused: u32, + pub _unused: [u32; 3], } /// Fade-out @@ -86,15 +60,13 @@ pub struct FadeWhite { #[derive(Zeroable, Pod)] #[repr(C)] pub struct FadeOut { - pub pos_matrix: Matrix4x4, - pub prev: PanelFadeImageUniforms, - pub cur: PanelFadeImageUniforms, - pub next: PanelFadeImageUniforms, - pub fade_duration: f32, - pub progress: f32, - pub strength: f32, + pub pos_matrix: Matrix4x4, + pub image_ratio: Vec2, + pub progress: f32, + pub alpha: f32, + pub strength: f32, - pub _unused: u32, + pub _unused: [u32; 3], } /// Fade-in @@ -102,15 +74,13 @@ pub struct FadeOut { #[derive(Zeroable, Pod)] #[repr(C)] pub struct FadeIn { - pub pos_matrix: Matrix4x4, - pub prev: PanelFadeImageUniforms, - pub cur: PanelFadeImageUniforms, - pub next: PanelFadeImageUniforms, - pub fade_duration: f32, - pub progress: f32, - pub strength: f32, + pub pos_matrix: Matrix4x4, + pub image_ratio: Vec2, + pub progress: f32, + pub alpha: f32, + pub strength: f32, - pub _unused: u32, + pub _unused: [u32; 3], } /// Slide @@ -124,7 +94,7 @@ pub struct Slide { /// The maximum uniform size pub const MAX_UNIFORM_SIZE: usize = zsw_util::array_max(&[ size_of::(), - size_of::(), + size_of::(), size_of::(), size_of::(), size_of::(), diff --git a/zsw/src/panel/state/fade.rs b/zsw/src/panel/state/fade.rs index f8fd38a..c3b6577 100644 --- a/zsw/src/panel/state/fade.rs +++ b/zsw/src/panel/state/fade.rs @@ -4,7 +4,7 @@ pub mod images; // Exports -pub use self::images::{PanelFadeImage, PanelFadeImages, PanelFadeImagesShared}; +pub use self::images::{PanelFadeImage, PanelFadeImageSlot, PanelFadeImages, PanelFadeImagesShared}; // Imports use { diff --git a/zsw/src/panel/state/fade/images.rs b/zsw/src/panel/state/fade/images.rs index 3fb82b8..28ccc4b 100644 --- a/zsw/src/panel/state/fade/images.rs +++ b/zsw/src/panel/state/fade/images.rs @@ -43,9 +43,6 @@ pub struct PanelFadeImages { /// Image sampler pub image_sampler: OnceCell, - /// Image bind group - pub image_bind_group: OnceCell, - /// Next image pub next_image: Loadable, } @@ -56,6 +53,9 @@ pub struct PanelFadeImage { /// Texture view pub texture_view: wgpu::TextureView, + /// Bind group + pub bind_group: OnceCell, + /// Swap direction pub swap_dir: bool, @@ -63,20 +63,47 @@ pub struct PanelFadeImage { pub path: Arc, } +impl PanelFadeImage { + /// Gets the bind group, or initializes it, if uninitialized + pub async fn bind_group( + &self, + wgpu: &Wgpu, + sampler: &wgpu::Sampler, + shared: &PanelFadeImagesShared, + ) -> &wgpu::BindGroup { + self.bind_group + .get_or_init(async || { + let layout = shared.image_bind_group_layout(wgpu).await; + self::create_image_bind_group(wgpu, layout, &self.texture_view, sampler) + }) + .await + } +} + impl PanelFadeImages { /// Creates a new panel #[must_use] pub fn new() -> Self { Self { - prev: None, - cur: None, - next: None, - image_sampler: OnceCell::new(), - image_bind_group: OnceCell::new(), - next_image: Loadable::new(), + prev: None, + cur: None, + next: None, + image_sampler: OnceCell::new(), + next_image: Loadable::new(), } } + /// Returns an iterator over all images + pub fn iter(&self) -> impl Iterator { + [ + (PanelFadeImageSlot::Prev, &self.prev), + (PanelFadeImageSlot::Cur, &self.cur), + (PanelFadeImageSlot::Next, &self.next), + ] + .into_iter() + .filter_map(|(slot, img)| img.as_ref().map(|img| (slot, img))) + } + /// Steps to the previous image, if any /// /// If successful, starts loading any missing images @@ -87,7 +114,6 @@ impl PanelFadeImages { mem::swap(&mut self.cur, &mut self.next); mem::swap(&mut self.prev, &mut self.cur); self.prev = None; - self.image_bind_group = OnceCell::new(); self.load_missing(playlist_player, wgpu); Ok(()) @@ -107,7 +133,6 @@ impl PanelFadeImages { mem::swap(&mut self.prev, &mut self.cur); mem::swap(&mut self.cur, &mut self.next); self.next = None; - self.image_bind_group = OnceCell::new(); self.load_missing(playlist_player, wgpu); Ok(()) @@ -120,19 +145,6 @@ impl PanelFadeImages { .await } - /// Gets the image bind group, or initializes it, if uninitialized - pub async fn image_bind_group(&self, wgpu: &Wgpu, shared: &PanelFadeImagesShared) -> &wgpu::BindGroup { - self.image_bind_group - .get_or_init(async || { - let [prev, cur, next] = [&self.prev, &self.cur, &self.next] - .map(|img| img.as_ref().map_or(&wgpu.empty_texture_view, |img| &img.texture_view)); - let image_sampler = self.image_sampler(wgpu).await; - let layout = shared.image_bind_group_layout(wgpu).await; - self::create_image_bind_group(wgpu, layout, prev, cur, next, image_sampler) - }) - .await - } - /// Loads any missing images, prioritizing the current, then next, then previous. /// /// Requests images if missing any. @@ -164,9 +176,9 @@ impl PanelFadeImages { // Get which slot to load the image into let slot = { match res.playlist_pos { - pos if Some(pos) == playlist_player.prev_pos() => Some(Slot::Prev), - pos if pos == playlist_player.cur_pos() => Some(Slot::Cur), - pos if pos == playlist_player.next_pos() => Some(Slot::Next), + pos if Some(pos) == playlist_player.prev_pos() => Some(PanelFadeImageSlot::Prev), + pos if pos == playlist_player.cur_pos() => Some(PanelFadeImageSlot::Cur), + pos if pos == playlist_player.next_pos() => Some(PanelFadeImageSlot::Next), pos => { tracing::warn!( pos, @@ -192,14 +204,14 @@ impl PanelFadeImages { texture_view, swap_dir: rand::random(), path: res.path, + bind_group: OnceCell::new(), }; match slot { - Slot::Prev => self.prev = Some(image), - Slot::Cur => self.cur = Some(image), - Slot::Next => self.next = Some(image), + PanelFadeImageSlot::Prev => self.prev = Some(image), + PanelFadeImageSlot::Cur => self.cur = Some(image), + PanelFadeImageSlot::Next => self.next = Some(image), } - self.image_bind_group = OnceCell::new(); } } @@ -255,8 +267,8 @@ impl PanelFadeImages { } /// Image slot -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -enum Slot { +#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, Debug)] +pub enum PanelFadeImageSlot { Prev, Cur, Next, @@ -310,26 +322,6 @@ fn create_bind_group_layout(wgpu: &Wgpu) -> wgpu::BindGroupLayout { wgpu::BindGroupLayoutEntry { binding: 1, visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Texture { - multisampled: false, - view_dimension: wgpu::TextureViewDimension::D2, - sample_type: wgpu::TextureSampleType::Float { filterable: true }, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 2, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Texture { - multisampled: false, - view_dimension: wgpu::TextureViewDimension::D2, - sample_type: wgpu::TextureSampleType::Float { filterable: true }, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 3, - visibility: wgpu::ShaderStages::FRAGMENT, ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), count: None, }, @@ -343,9 +335,7 @@ fn create_bind_group_layout(wgpu: &Wgpu) -> wgpu::BindGroupLayout { fn create_image_bind_group( wgpu: &Wgpu, bind_group_layout: &wgpu::BindGroupLayout, - view_prev: &wgpu::TextureView, - view_cur: &wgpu::TextureView, - view_next: &wgpu::TextureView, + view: &wgpu::TextureView, sampler: &wgpu::Sampler, ) -> wgpu::BindGroup { let descriptor = wgpu::BindGroupDescriptor { @@ -354,18 +344,10 @@ fn create_image_bind_group( entries: &[ wgpu::BindGroupEntry { binding: 0, - resource: wgpu::BindingResource::TextureView(view_prev), + resource: wgpu::BindingResource::TextureView(view), }, wgpu::BindGroupEntry { binding: 1, - resource: wgpu::BindingResource::TextureView(view_cur), - }, - wgpu::BindGroupEntry { - binding: 2, - resource: wgpu::BindingResource::TextureView(view_next), - }, - wgpu::BindGroupEntry { - binding: 3, resource: wgpu::BindingResource::Sampler(sampler), }, ],