Fade shader now renders the 3 images separately.

This commit is contained in:
Filipe Rodrigues 2025-09-17 18:30:16 +01:00
parent 40188d9f4e
commit 9cf59df867
Signed by: zenithsiz
SSH Key Fingerprint: SHA256:Mb5ppb3Sh7IarBO/sBTXLHbYEOz37hJAlslLQPPAPaU
12 changed files with 327 additions and 422 deletions

View File

@ -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<f32>,
image_ratio: vec2<f32>,
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<uniform> uniforms: Uniforms;
/// Vertex output
struct VertexOutput {
@builtin(position)
pos: vec4<f32>,
@location(0)
uvs: vec2<f32>,
};
@vertex
fn vs_main(in: VertexInput) -> VertexOutput {
return vertex::main(in);
fn vs_main(
@location(0) pos: vec2<f32>,
@location(1) uvs: vec2<f32>,
) -> VertexOutput {
var out: VertexOutput;
out.pos = uniforms.pos_matrix * vec4<f32>(pos, 0.0, 1.0);
out.uvs = uvs;
return out;
}
// Image
@group(1) @binding(0) var image: texture_2d<f32>;
@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<f32> {
// 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);
}

View File

@ -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<f32>;
@group(1) @binding(1) var image_cur: texture_2d<f32>;
@group(1) @binding(2) var image_next: texture_2d<f32>;
@group(1) @binding(3) var image_sampler: sampler;
struct Sampled {
color: vec4<f32>,
uvs : vec2<f32>,
}
// Samples an image
fn sample(image: texture_2d<f32>, in_uvs: vec2<f32>, 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<f32>(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<f32>(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<f32>(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;
}

View File

@ -1,25 +0,0 @@
//! Stage Input/Output
// Vertex input
struct VertexInput {
@location(0)
pos: vec2<f32>,
@location(1)
uvs: vec2<f32>,
};
// Vertex output / Frag Input
struct VertexOutput {
@builtin(position)
pos: vec4<f32>,
@location(0)
uvs: vec2<f32>,
};
// Frag output
struct FragOutput {
@location(0)
color: vec4<f32>,
};

View File

@ -1,30 +0,0 @@
//! Uniforms
/// Uniforms for each image
struct ImageUniforms {
image_ratio: vec2<f32>,
swap_dir: u32,
}
/// Uniforms
struct Uniforms {
pos_matrix: mat4x4<f32>,
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<uniform> uniforms: Uniforms;

View File

@ -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<f32>(in.pos, 0.0, 1.0);
out.uvs = in.uvs;
return out;
}

View File

@ -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<RenderPanelsF
panels: HashMap<usize, DurationPanelIdxTree>,
}
struct DurationPanelIdxTree {
geometries: HashSet<usize>,
geometries: HashMap<usize, DurationPanelGeometryIdxTree>,
}
struct DurationPanelGeometryIdxTree {
images: HashSet<PanelFadeImageSlot>,
}
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<RenderPanelsF
.flat_map(|frame_time| {
frame_time.panels.iter().map(|(&panel_idx, panel)| {
let panel = DurationPanelIdxTree {
geometries: panel.geometries.keys().copied().collect(),
geometries: panel
.geometries
.iter()
.map(|(&geometry_idx, geometry)| {
let geometry = DurationPanelGeometryIdxTree {
#[expect(clippy::match_same_arms, reason = "They won't be in the future")]
images:
match geometry {
RenderPanelGeometryFrameTime::None(_) => 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<RenderPanelsF
iter::chain(
// Panel specific indices
[DurationPanelIdx::UpdatePanel, DurationPanelIdx::CreateRenderPipeline],
panel.geometries.into_iter().flat_map(|geometry_idx| {
panel.geometries.into_iter().flat_map(|(geometry_idx, geometry)| {
// Panel & geometry specific indices
iter_chain!(
[
@ -59,11 +81,13 @@ pub fn draw(ui: &mut egui::Ui, render_frame_times: &mut FrameTimes<RenderPanelsF
DurationPanelGeometryNoneIdx::Draw,
]
.map(move |inner| DurationPanelGeometryIdx::None { inner }),
[
DurationPanelGeometryFadeIdx::WriteUniforms,
DurationPanelGeometryFadeIdx::Draw,
]
.map(move |inner| DurationPanelGeometryIdx::Fade { inner }),
geometry.images.into_iter().flat_map(|image_slot| {
[
DurationPanelGeometryFadeIdx::WriteUniforms,
DurationPanelGeometryFadeIdx::Draw,
]
.map(move |inner| DurationPanelGeometryIdx::Fade { image_slot, inner })
}),
[
DurationPanelGeometrySlideIdx::WriteUniforms,
DurationPanelGeometrySlideIdx::Draw,
@ -152,16 +176,23 @@ impl DurationPanelIdx {
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, Debug)]
enum DurationPanelGeometryIdx {
None { inner: DurationPanelGeometryNoneIdx },
Fade { inner: DurationPanelGeometryFadeIdx },
Slide { inner: DurationPanelGeometrySlideIdx },
None {
inner: DurationPanelGeometryNoneIdx,
},
Fade {
image_slot: PanelFadeImageSlot,
inner: DurationPanelGeometryFadeIdx,
},
Slide {
inner: DurationPanelGeometrySlideIdx,
},
}
impl DurationPanelGeometryIdx {
pub fn name(self, panel_idx: usize, geometry_idx: usize) -> 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<Duration> {
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<Duration> {
pub fn duration(self, frame_time: &RenderPanelGeometryFadeImageFrameTime) -> Option<Duration> {
match self {
Self::WriteUniforms => Some(frame_time.write_uniforms),
Self::Draw => Some(frame_time.draw),

View File

@ -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<PanelFadeImageSlot, RenderPanelGeometryFadeImageFrameTime>,
}
/// Render panel geometry fade image frame time.
#[derive(Clone, Debug)]
pub struct RenderPanelGeometryFadeImageFrameTime {
pub write_uniforms: Duration,
pub draw: Duration,
}

View File

@ -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<PanelGeometryNoneUniforms>,
pub fade: OnceCell<PanelGeometryFadeUniforms>,
pub fade: PanelGeometryFadeUniforms,
pub slide: OnceCell<PanelGeometrySlideUniforms>,
}
@ -29,8 +34,15 @@ pub struct PanelGeometryNoneUniforms {
}
/// Panel geometry fade uniforms
#[derive(Debug)]
#[derive(Default, Debug)]
pub struct PanelGeometryFadeUniforms {
/// Images
pub images: HashMap<PanelFadeImageSlot, PanelGeometryFadeImageUniforms>,
}
/// Panel geometry fade image uniforms
#[derive(Debug)]
pub struct PanelGeometryFadeImageUniforms {
/// Buffer
pub buffer: wgpu::Buffer,

View File

@ -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

View File

@ -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::<None>(),
size_of::<Fade>(),
size_of::<FadeBasic>(),
size_of::<FadeWhite>(),
size_of::<FadeOut>(),
size_of::<FadeIn>(),

View File

@ -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 {

View File

@ -43,9 +43,6 @@ pub struct PanelFadeImages {
/// Image sampler
pub image_sampler: OnceCell<wgpu::Sampler>,
/// Image bind group
pub image_bind_group: OnceCell<wgpu::BindGroup>,
/// Next image
pub next_image: Loadable<ImageLoadRes>,
}
@ -56,6 +53,9 @@ pub struct PanelFadeImage {
/// Texture view
pub texture_view: wgpu::TextureView,
/// Bind group
pub bind_group: OnceCell<wgpu::BindGroup>,
/// Swap direction
pub swap_dir: bool,
@ -63,20 +63,47 @@ pub struct PanelFadeImage {
pub path: Arc<Path>,
}
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<Item = (PanelFadeImageSlot, &PanelFadeImage)> {
[
(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),
},
],