mirror of
https://github.com/Zenithsiz/zsw.git
synced 2026-02-03 09:50:31 +00:00
Compare commits
20 Commits
f9de217a55
...
fe7a002a5f
| Author | SHA1 | Date | |
|---|---|---|---|
| fe7a002a5f | |||
| 84ff5bf4df | |||
| dba540ee87 | |||
| cac0b400bf | |||
| 82d39ac190 | |||
| 9a32a9addb | |||
| 1beee7dc11 | |||
| 1bd36da1fa | |||
| cd52e98000 | |||
| f124a34320 | |||
| 23c464949d | |||
| 3e1df2bf2d | |||
| 736686a31e | |||
| 9cf59df867 | |||
| 40188d9f4e | |||
| 4702a15f16 | |||
| dec17bb280 | |||
| 085fc09dab | |||
| b90d926a6a | |||
| 84f3cfc252 |
@ -170,3 +170,14 @@ where
|
||||
tracing::warn!("Unable to spawn task {name:?}: {}", err.pretty());
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterator chain
|
||||
pub macro iter_chain {
|
||||
($only:expr $(,)?) => {
|
||||
$only
|
||||
},
|
||||
|
||||
($first:expr, $($rest:expr),* $(,)?) => {
|
||||
std::iter::chain($first, $crate::iter_chain!($($rest,)*))
|
||||
},
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
@ -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>,
|
||||
};
|
||||
@ -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;
|
||||
@ -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;
|
||||
}
|
||||
@ -2,10 +2,22 @@
|
||||
|
||||
// Imports
|
||||
use {
|
||||
crate::metrics::{FrameTimes, RenderPanelFrameTime, RenderPanelGeometryFrameTime, RenderPanelsFrameTime},
|
||||
crate::{
|
||||
metrics::{
|
||||
FrameTimes,
|
||||
RenderPanelFrameTime,
|
||||
RenderPanelGeometryFadeImageFrameTime,
|
||||
RenderPanelGeometryFrameTime,
|
||||
RenderPanelGeometryNoneFrameTime,
|
||||
RenderPanelGeometrySlideFrameTime,
|
||||
RenderPanelsFrameTime,
|
||||
},
|
||||
panel::state::fade::PanelFadeImageSlot,
|
||||
},
|
||||
core::{iter, time::Duration},
|
||||
itertools::Itertools,
|
||||
std::collections::{HashMap, HashSet},
|
||||
zsw_util::iter_chain,
|
||||
};
|
||||
|
||||
/// Draws the render panel frame times
|
||||
@ -18,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
|
||||
@ -26,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)
|
||||
})
|
||||
@ -42,10 +73,28 @@ 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
|
||||
[DurationPanelGeometryIdx::WriteUniforms, DurationPanelGeometryIdx::Draw]
|
||||
.map(move |inner| DurationPanelIdx::Geometries { geometry_idx, inner })
|
||||
iter_chain!(
|
||||
[
|
||||
DurationPanelGeometryNoneIdx::WriteUniforms,
|
||||
DurationPanelGeometryNoneIdx::Draw,
|
||||
]
|
||||
.map(move |inner| DurationPanelGeometryIdx::None { 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,
|
||||
]
|
||||
.map(move |inner| DurationPanelGeometryIdx::Slide { inner }),
|
||||
)
|
||||
.map(move |inner| DurationPanelIdx::Geometries { geometry_idx, inner })
|
||||
}),
|
||||
)
|
||||
.map(move |inner| DurationIdx::Panels { panel_idx, inner })
|
||||
@ -127,19 +176,104 @@ impl DurationPanelIdx {
|
||||
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, Debug)]
|
||||
enum DurationPanelGeometryIdx {
|
||||
WriteUniforms,
|
||||
Draw,
|
||||
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::WriteUniforms => format!("[Panel${panel_idx}] [Geometry${geometry_idx}] Write uniforms"),
|
||||
Self::Draw => format!("[Panel${panel_idx}] [Geometry${geometry_idx}] Draw"),
|
||||
Self::None { 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),
|
||||
}
|
||||
}
|
||||
|
||||
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 { 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, Debug)]
|
||||
enum DurationPanelGeometryNoneIdx {
|
||||
WriteUniforms,
|
||||
Draw,
|
||||
}
|
||||
|
||||
impl DurationPanelGeometryNoneIdx {
|
||||
pub fn name(self, panel_idx: usize, geometry_idx: usize) -> String {
|
||||
match self {
|
||||
Self::WriteUniforms =>
|
||||
format!("[Panel${panel_idx}] [Geometry${geometry_idx}] [Shader$None] Write uniforms"),
|
||||
Self::Draw => format!("[Panel${panel_idx}] [Geometry${geometry_idx}] [Shader$None] Draw"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn duration(self, frame_time: &RenderPanelGeometryNoneFrameTime) -> Option<Duration> {
|
||||
match self {
|
||||
Self::WriteUniforms => Some(frame_time.write_uniforms),
|
||||
Self::Draw => Some(frame_time.draw),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, Debug)]
|
||||
enum DurationPanelGeometryFadeIdx {
|
||||
WriteUniforms,
|
||||
Draw,
|
||||
}
|
||||
|
||||
impl DurationPanelGeometryFadeIdx {
|
||||
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] [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: &RenderPanelGeometryFadeImageFrameTime) -> Option<Duration> {
|
||||
match self {
|
||||
Self::WriteUniforms => Some(frame_time.write_uniforms),
|
||||
Self::Draw => Some(frame_time.draw),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, Debug)]
|
||||
enum DurationPanelGeometrySlideIdx {
|
||||
WriteUniforms,
|
||||
Draw,
|
||||
}
|
||||
|
||||
impl DurationPanelGeometrySlideIdx {
|
||||
pub fn name(self, panel_idx: usize, geometry_idx: usize) -> String {
|
||||
match self {
|
||||
Self::WriteUniforms =>
|
||||
format!("[Panel${panel_idx}] [Geometry${geometry_idx}] [Shader$Slide] Write uniforms"),
|
||||
Self::Draw => format!("[Panel${panel_idx}] [Geometry${geometry_idx}] [Shader$Slide] Draw"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn duration(self, frame_time: &RenderPanelGeometrySlideFrameTime) -> Option<Duration> {
|
||||
match self {
|
||||
Self::WriteUniforms => Some(frame_time.write_uniforms),
|
||||
Self::Draw => Some(frame_time.draw),
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
// Imports
|
||||
use {
|
||||
crate::panel::state::fade::PanelFadeImageSlot,
|
||||
core::{ops::DerefMut, time::Duration},
|
||||
std::collections::{HashMap, VecDeque},
|
||||
tokio::sync::{Mutex, MutexGuard},
|
||||
@ -158,7 +159,36 @@ pub struct RenderPanelFrameTime {
|
||||
|
||||
/// Render panel geometry frame time.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RenderPanelGeometryFrameTime {
|
||||
#[derive(derive_more::From)]
|
||||
pub enum RenderPanelGeometryFrameTime {
|
||||
None(RenderPanelGeometryNoneFrameTime),
|
||||
Fade(RenderPanelGeometryFadeFrameTime),
|
||||
Slide(RenderPanelGeometrySlideFrameTime),
|
||||
}
|
||||
|
||||
/// Render panel geometry none frame time.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RenderPanelGeometryNoneFrameTime {
|
||||
pub write_uniforms: Duration,
|
||||
pub draw: Duration,
|
||||
}
|
||||
|
||||
/// 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,
|
||||
}
|
||||
|
||||
/// Render panel geometry slide frame time.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RenderPanelGeometrySlideFrameTime {
|
||||
pub write_uniforms: Duration,
|
||||
pub draw: Duration,
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@ pub mod state;
|
||||
|
||||
// Exports
|
||||
pub use self::{
|
||||
geometry::{PanelGeometry, PanelGeometryUniforms},
|
||||
geometry::PanelGeometry,
|
||||
panels::Panels,
|
||||
renderer::{PanelFadeShader, PanelShader, PanelSlideShader, PanelsRenderer, PanelsRendererShared},
|
||||
state::PanelState,
|
||||
|
||||
@ -1,8 +1,14 @@
|
||||
//! Panel geometry
|
||||
|
||||
// Imports
|
||||
use {std::collections::HashMap, winit::window::WindowId};
|
||||
|
||||
use {
|
||||
super::state::{fade, none, slide},
|
||||
crate::panel::state::fade::PanelFadeImageSlot,
|
||||
std::collections::HashMap,
|
||||
tokio::sync::OnceCell,
|
||||
winit::window::WindowId,
|
||||
zsw_wgpu::Wgpu,
|
||||
};
|
||||
|
||||
/// Panel geometry
|
||||
#[derive(Debug)]
|
||||
@ -12,8 +18,72 @@ pub struct PanelGeometry {
|
||||
}
|
||||
|
||||
/// Panel geometry uniforms
|
||||
#[derive(Debug)]
|
||||
#[derive(Default, Debug)]
|
||||
pub struct PanelGeometryUniforms {
|
||||
pub none: OnceCell<PanelGeometryNoneUniforms>,
|
||||
pub fade: PanelGeometryFadeUniforms,
|
||||
pub slide: OnceCell<PanelGeometrySlideUniforms>,
|
||||
}
|
||||
|
||||
impl PanelGeometryUniforms {
|
||||
/// Returns the none uniforms
|
||||
pub async fn none(&self, wgpu: &Wgpu, layout: &wgpu::BindGroupLayout) -> &PanelGeometryNoneUniforms {
|
||||
self.none
|
||||
.get_or_init(async || none::create_geometry_uniforms(wgpu, layout))
|
||||
.await
|
||||
}
|
||||
|
||||
/// Returns the slide uniforms
|
||||
pub async fn slide(&self, wgpu: &Wgpu, layout: &wgpu::BindGroupLayout) -> &PanelGeometrySlideUniforms {
|
||||
self.slide
|
||||
.get_or_init(async || slide::create_geometry_uniforms(wgpu, layout))
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
/// Panel geometry none uniforms
|
||||
#[derive(Debug)]
|
||||
pub struct PanelGeometryNoneUniforms {
|
||||
/// Buffer
|
||||
pub buffer: wgpu::Buffer,
|
||||
|
||||
/// Bind group
|
||||
pub bind_group: wgpu::BindGroup,
|
||||
}
|
||||
|
||||
/// Panel geometry fade uniforms
|
||||
#[derive(Default, Debug)]
|
||||
pub struct PanelGeometryFadeUniforms {
|
||||
/// Images
|
||||
pub images: HashMap<PanelFadeImageSlot, PanelGeometryFadeImageUniforms>,
|
||||
}
|
||||
impl PanelGeometryFadeUniforms {
|
||||
/// Returns an image's uniforms
|
||||
pub fn image(
|
||||
&mut self,
|
||||
wgpu: &Wgpu,
|
||||
layout: &wgpu::BindGroupLayout,
|
||||
slot: PanelFadeImageSlot,
|
||||
) -> &mut PanelGeometryFadeImageUniforms {
|
||||
self.images
|
||||
.entry(slot)
|
||||
.or_insert_with(|| fade::images::create_image_geometry_uniforms(wgpu, layout))
|
||||
}
|
||||
}
|
||||
|
||||
/// Panel geometry fade image uniforms
|
||||
#[derive(Debug)]
|
||||
pub struct PanelGeometryFadeImageUniforms {
|
||||
/// Buffer
|
||||
pub buffer: wgpu::Buffer,
|
||||
|
||||
/// Bind group
|
||||
pub bind_group: wgpu::BindGroup,
|
||||
}
|
||||
|
||||
/// Panel geometry slide uniforms
|
||||
#[derive(Debug)]
|
||||
pub struct PanelGeometrySlideUniforms {
|
||||
/// Buffer
|
||||
pub buffer: wgpu::Buffer,
|
||||
|
||||
|
||||
@ -1,20 +1,37 @@
|
||||
//! Panels renderer
|
||||
|
||||
// Modules
|
||||
mod uniform;
|
||||
pub mod uniform;
|
||||
mod vertex;
|
||||
|
||||
// Exports
|
||||
pub use self::{uniform::MAX_UNIFORM_SIZE, vertex::PanelVertex};
|
||||
pub use self::vertex::PanelVertex;
|
||||
|
||||
// Imports
|
||||
use {
|
||||
self::uniform::PanelFadeImageUniforms,
|
||||
super::{PanelGeometryUniforms, PanelState, Panels, state::fade::PanelFadeImagesShared},
|
||||
super::{
|
||||
Panel,
|
||||
PanelState,
|
||||
Panels,
|
||||
geometry::{
|
||||
PanelGeometryFadeUniforms,
|
||||
PanelGeometryNoneUniforms,
|
||||
PanelGeometrySlideUniforms,
|
||||
PanelGeometryUniforms,
|
||||
},
|
||||
state::{
|
||||
PanelFadeState,
|
||||
PanelNoneState,
|
||||
PanelSlideState,
|
||||
fade::{PanelFadeImageSlot, PanelFadeImagesShared},
|
||||
none::PanelNoneImagesShared,
|
||||
slide::PanelSlideImagesShared,
|
||||
},
|
||||
},
|
||||
crate::{
|
||||
display::DisplayGeometry,
|
||||
metrics::{self, Metrics},
|
||||
panel::{PanelGeometry, state::fade::PanelFadeImage},
|
||||
panel::PanelGeometry,
|
||||
time,
|
||||
},
|
||||
app_error::Context,
|
||||
@ -46,11 +63,14 @@ pub struct PanelsRendererShared {
|
||||
/// Index buffer
|
||||
indices: wgpu::Buffer,
|
||||
|
||||
/// Uniforms bind group layout
|
||||
uniforms_bind_group_layout: wgpu::BindGroupLayout,
|
||||
/// None
|
||||
none: PanelNoneImagesShared,
|
||||
|
||||
/// Fade
|
||||
fade: PanelFadeImagesShared,
|
||||
|
||||
/// Slide
|
||||
slide: PanelSlideImagesShared,
|
||||
}
|
||||
|
||||
impl PanelsRendererShared {
|
||||
@ -60,14 +80,17 @@ impl PanelsRendererShared {
|
||||
let indices = self::create_indices(wgpu);
|
||||
let vertices = self::create_vertices(wgpu);
|
||||
|
||||
let uniforms_bind_group_layout = self::create_uniforms_bind_group_layout(wgpu);
|
||||
let none = PanelNoneImagesShared::new(wgpu);
|
||||
let fade = PanelFadeImagesShared::new(wgpu);
|
||||
let slide = PanelSlideImagesShared::new(wgpu);
|
||||
|
||||
Self {
|
||||
render_pipelines: Mutex::new(HashMap::new()),
|
||||
vertices,
|
||||
indices,
|
||||
uniforms_bind_group_layout,
|
||||
fade: PanelFadeImagesShared::default(),
|
||||
none,
|
||||
fade,
|
||||
slide,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -113,7 +136,6 @@ impl PanelsRenderer {
|
||||
}
|
||||
|
||||
/// Renders a panel
|
||||
#[expect(clippy::too_many_lines, reason = "TODO: Split it up")]
|
||||
pub async fn render(
|
||||
&self,
|
||||
frame: &mut FrameRender,
|
||||
@ -178,128 +200,21 @@ impl PanelsRenderer {
|
||||
.enumerate()
|
||||
.map(|(panel_idx, panel)| async move { (panel_idx, panel.lock().await) })
|
||||
.collect::<FuturesUnordered<_>>();
|
||||
let mut panel_metrics = HashMap::new();
|
||||
let mut panels_metrics = HashMap::new();
|
||||
while let Some((panel_idx, mut panel)) = panels.next().await {
|
||||
let panel = &mut *panel;
|
||||
|
||||
// Update the panel before drawing it
|
||||
#[time(update_panel)]
|
||||
let () = match &mut panel.state {
|
||||
PanelState::None(_) => (),
|
||||
PanelState::Fade(state) => state.update(wgpu).await,
|
||||
#[expect(clippy::match_same_arms, reason = "We'll be changing them soon")]
|
||||
PanelState::Slide(_) => (),
|
||||
};
|
||||
|
||||
// If the panel images are empty, there's no sense in rendering it either
|
||||
#[expect(clippy::match_same_arms, reason = "We'll be changing them soon")]
|
||||
let are_images_empty = match &panel.state {
|
||||
PanelState::None(_) => false,
|
||||
PanelState::Fade(state) => state.images().is_empty(),
|
||||
PanelState::Slide(_) => false,
|
||||
};
|
||||
if are_images_empty {
|
||||
continue;
|
||||
}
|
||||
|
||||
let render_pipeline_id = match &panel.state {
|
||||
PanelState::None(_) => RenderPipelineId::None,
|
||||
PanelState::Fade(state) => RenderPipelineId::Fade(match state.shader() {
|
||||
PanelFadeShader::Basic => RenderPipelineFadeId::Basic,
|
||||
PanelFadeShader::White { .. } => RenderPipelineFadeId::White,
|
||||
PanelFadeShader::Out { .. } => RenderPipelineFadeId::Out,
|
||||
PanelFadeShader::In { .. } => RenderPipelineFadeId::In,
|
||||
}),
|
||||
PanelState::Slide(state) => RenderPipelineId::Slide(match state.shader() {
|
||||
PanelSlideShader::Basic => RenderPipelineSlideId::Basic,
|
||||
}),
|
||||
};
|
||||
|
||||
#[time(create_render_pipeline)]
|
||||
let render_pipeline = match shared.render_pipelines.lock().await.entry(render_pipeline_id) {
|
||||
hash_map::Entry::Occupied(entry) => Arc::clone(entry.get()),
|
||||
hash_map::Entry::Vacant(entry) => {
|
||||
let bind_group_layouts = match panel.state {
|
||||
PanelState::None(_) => &[&shared.uniforms_bind_group_layout] as &[_],
|
||||
PanelState::Fade(_) => &[
|
||||
&shared.uniforms_bind_group_layout,
|
||||
shared.fade.image_bind_group_layout(wgpu).await,
|
||||
],
|
||||
PanelState::Slide(_) => &[&shared.uniforms_bind_group_layout],
|
||||
};
|
||||
|
||||
let render_pipeline = self::create_render_pipeline(
|
||||
wgpu_renderer,
|
||||
wgpu,
|
||||
render_pipeline_id,
|
||||
bind_group_layouts,
|
||||
panel.state.shader(),
|
||||
self.msaa_samples,
|
||||
)
|
||||
.context("Unable to create render pipeline")?;
|
||||
|
||||
Arc::clone(entry.insert(Arc::new(render_pipeline)))
|
||||
},
|
||||
};
|
||||
|
||||
// Bind the pipeline for the specific shader
|
||||
render_pass.set_pipeline(&render_pipeline);
|
||||
|
||||
// Bind the extra bind groups
|
||||
match &mut panel.state {
|
||||
PanelState::None(_panel_state) => (),
|
||||
PanelState::Fade(panel_state) =>
|
||||
render_pass.set_bind_group(1, panel_state.images().image_bind_group(wgpu, &shared.fade).await, &[]),
|
||||
PanelState::Slide(_panel_state) => (),
|
||||
}
|
||||
|
||||
// The display might have changed asynchronously from the panel geometries,
|
||||
// so resize it to ensure we have a panel geometry for each display geometry.
|
||||
let display = panel.display.read().await;
|
||||
panel
|
||||
.geometries
|
||||
.resize_with(display.geometries.len(), PanelGeometry::new);
|
||||
|
||||
let geometry_metrics = panel_metrics
|
||||
.entry(panel_idx)
|
||||
.or_insert_with(|| metrics::RenderPanelFrameTime {
|
||||
update_panel,
|
||||
create_render_pipeline,
|
||||
geometries: HashMap::new(),
|
||||
});
|
||||
for (geometry_idx, (display_geometry, panel_geometry)) in
|
||||
display.geometries.iter().zip_eq(&mut panel.geometries).enumerate()
|
||||
{
|
||||
// If this geometry is outside our window, we can safely ignore it
|
||||
if !display_geometry.intersects_window(window_geometry) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Write and bind the uniforms
|
||||
#[time(write_uniforms)]
|
||||
Self::write_bind_uniforms(
|
||||
wgpu,
|
||||
shared,
|
||||
frame.surface_size,
|
||||
&panel.state,
|
||||
window_geometry,
|
||||
window,
|
||||
display_geometry,
|
||||
panel_geometry,
|
||||
&mut render_pass,
|
||||
);
|
||||
|
||||
// Finally draw
|
||||
#[time(draw)]
|
||||
render_pass.draw_indexed(0..6, 0, 0..1);
|
||||
|
||||
_ = geometry_metrics
|
||||
.geometries
|
||||
.insert(geometry_idx, metrics::RenderPanelGeometryFrameTime {
|
||||
write_uniforms,
|
||||
draw,
|
||||
});
|
||||
}
|
||||
self.render_panel(
|
||||
wgpu,
|
||||
shared,
|
||||
wgpu_renderer,
|
||||
frame.surface_size,
|
||||
window,
|
||||
window_geometry,
|
||||
&mut render_pass,
|
||||
panel_idx,
|
||||
&mut panel,
|
||||
&mut panels_metrics,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
metrics
|
||||
@ -308,138 +223,363 @@ impl PanelsRenderer {
|
||||
.add(metrics::RenderPanelsFrameTime {
|
||||
create_render_pass,
|
||||
lock_panels,
|
||||
panels: panel_metrics,
|
||||
panels: panels_metrics,
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes and binds the uniforms
|
||||
pub fn write_bind_uniforms(
|
||||
/// Renders a panel
|
||||
async fn render_panel(
|
||||
&self,
|
||||
wgpu: &Wgpu,
|
||||
shared: &PanelsRendererShared,
|
||||
wgpu_renderer: &WgpuRenderer,
|
||||
surface_size: PhysicalSize<u32>,
|
||||
window: &Window,
|
||||
window_geometry: Rect<i32, u32>,
|
||||
render_pass: &mut wgpu::RenderPass<'_>,
|
||||
panel_idx: usize,
|
||||
panel: &mut Panel,
|
||||
panels_metrics: &mut HashMap<usize, metrics::RenderPanelFrameTime>,
|
||||
) -> Result<(), app_error::AppError> {
|
||||
// Update the panel before drawing it
|
||||
#[time(update_panel)]
|
||||
let () = match &mut panel.state {
|
||||
PanelState::None(_) => (),
|
||||
PanelState::Fade(state) => state.update(wgpu).await,
|
||||
#[expect(clippy::match_same_arms, reason = "We'll be changing them soon")]
|
||||
PanelState::Slide(_) => (),
|
||||
};
|
||||
|
||||
// If the panel images are empty, there's no sense in rendering it either
|
||||
#[expect(clippy::match_same_arms, reason = "We'll be changing them soon")]
|
||||
let are_images_empty = match &panel.state {
|
||||
PanelState::None(_) => false,
|
||||
PanelState::Fade(state) => state.images().is_empty(),
|
||||
PanelState::Slide(_) => false,
|
||||
};
|
||||
if are_images_empty {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let render_pipeline_id = match &panel.state {
|
||||
PanelState::None(_) => RenderPipelineId::None,
|
||||
PanelState::Fade(state) => RenderPipelineId::Fade(match state.shader() {
|
||||
PanelFadeShader::Basic => RenderPipelineFadeId::Basic,
|
||||
PanelFadeShader::White { .. } => RenderPipelineFadeId::White,
|
||||
PanelFadeShader::Out { .. } => RenderPipelineFadeId::Out,
|
||||
PanelFadeShader::In { .. } => RenderPipelineFadeId::In,
|
||||
}),
|
||||
PanelState::Slide(state) => RenderPipelineId::Slide(match state.shader() {
|
||||
PanelSlideShader::Basic => RenderPipelineSlideId::Basic,
|
||||
}),
|
||||
};
|
||||
|
||||
#[time(create_render_pipeline)]
|
||||
let render_pipeline = match shared.render_pipelines.lock().await.entry(render_pipeline_id) {
|
||||
hash_map::Entry::Occupied(entry) => Arc::clone(entry.get()),
|
||||
hash_map::Entry::Vacant(entry) => {
|
||||
let bind_group_layouts = match panel.state {
|
||||
PanelState::None(_) => &[&shared.none.geometry_uniforms_bind_group_layout] as &[_],
|
||||
PanelState::Fade(_) => &[
|
||||
&shared.fade.geometry_uniforms_bind_group_layout,
|
||||
shared.fade.image_bind_group_layout(wgpu).await,
|
||||
],
|
||||
PanelState::Slide(_) => &[&shared.slide.geometry_uniforms_bind_group_layout],
|
||||
};
|
||||
|
||||
let render_pipeline = self::create_render_pipeline(
|
||||
wgpu_renderer,
|
||||
wgpu,
|
||||
render_pipeline_id,
|
||||
bind_group_layouts,
|
||||
panel.state.shader(),
|
||||
self.msaa_samples,
|
||||
)
|
||||
.context("Unable to create render pipeline")?;
|
||||
|
||||
Arc::clone(entry.insert(Arc::new(render_pipeline)))
|
||||
},
|
||||
};
|
||||
|
||||
// Bind the pipeline for the specific shader
|
||||
render_pass.set_pipeline(&render_pipeline);
|
||||
|
||||
let panel_metrics = panels_metrics
|
||||
.entry(panel_idx)
|
||||
.or_insert_with(|| metrics::RenderPanelFrameTime {
|
||||
update_panel,
|
||||
create_render_pipeline,
|
||||
geometries: HashMap::new(),
|
||||
});
|
||||
|
||||
// Then render the panel
|
||||
Self::render_panel_geometries(
|
||||
wgpu,
|
||||
shared,
|
||||
surface_size,
|
||||
window,
|
||||
window_geometry,
|
||||
render_pass,
|
||||
panel,
|
||||
panel_metrics,
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Renders a panel's geometries
|
||||
async fn render_panel_geometries(
|
||||
wgpu: &Wgpu,
|
||||
shared: &PanelsRendererShared,
|
||||
surface_size: PhysicalSize<u32>,
|
||||
window: &Window,
|
||||
window_geometry: Rect<i32, u32>,
|
||||
render_pass: &mut wgpu::RenderPass<'_>,
|
||||
panel: &mut Panel,
|
||||
panel_metrics: &mut metrics::RenderPanelFrameTime,
|
||||
) {
|
||||
// The display might have changed asynchronously from the panel geometries,
|
||||
// so resize it to ensure we have a panel geometry for each display geometry.
|
||||
let display = panel.display.read().await;
|
||||
panel
|
||||
.geometries
|
||||
.resize_with(display.geometries.len(), PanelGeometry::new);
|
||||
|
||||
// Go through all geometries of the panel display and render each one
|
||||
for (geometry_idx, (display_geometry, panel_geometry)) in
|
||||
display.geometries.iter().zip_eq(&mut panel.geometries).enumerate()
|
||||
{
|
||||
// If this geometry is outside our window, we can safely ignore it
|
||||
if !display_geometry.intersects_window(window_geometry) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Render the panel geometry
|
||||
let geometry_uniforms = panel_geometry.uniforms.entry(window.id()).or_default();
|
||||
let geometry_metrics = Self::render_panel_geometry(
|
||||
wgpu,
|
||||
shared,
|
||||
surface_size,
|
||||
&panel.state,
|
||||
window_geometry,
|
||||
display_geometry,
|
||||
geometry_uniforms,
|
||||
render_pass,
|
||||
)
|
||||
.await;
|
||||
|
||||
_ = panel_metrics.geometries.insert(geometry_idx, geometry_metrics);
|
||||
}
|
||||
}
|
||||
|
||||
/// Renders a panel's geometry
|
||||
pub async fn render_panel_geometry(
|
||||
wgpu: &Wgpu,
|
||||
shared: &PanelsRendererShared,
|
||||
surface_size: PhysicalSize<u32>,
|
||||
panel_state: &PanelState,
|
||||
window_geometry: Rect<i32, u32>,
|
||||
window: &Window,
|
||||
display_geometry: &DisplayGeometry,
|
||||
panel_geometry: &mut PanelGeometry,
|
||||
geometry_uniforms: &mut PanelGeometryUniforms,
|
||||
render_pass: &mut wgpu::RenderPass<'_>,
|
||||
) {
|
||||
) -> metrics::RenderPanelGeometryFrameTime {
|
||||
// Calculate the position matrix for the panel
|
||||
let pos_matrix = display_geometry.pos_matrix(window_geometry, surface_size);
|
||||
let pos_matrix = uniform::Matrix4x4(pos_matrix.into());
|
||||
|
||||
// Writes uniforms `uniforms`
|
||||
let geometry_uniforms = panel_geometry
|
||||
.uniforms
|
||||
.entry(window.id())
|
||||
.or_insert_with(|| self::create_geometry_uniforms(wgpu, &shared.uniforms_bind_group_layout));
|
||||
let write_uniforms = |uniforms_bytes| {
|
||||
wgpu.queue.write_buffer(&geometry_uniforms.buffer, 0, uniforms_bytes);
|
||||
};
|
||||
macro write_uniforms($uniforms:expr) {
|
||||
write_uniforms(bytemuck::bytes_of(&$uniforms))
|
||||
}
|
||||
|
||||
match panel_state {
|
||||
PanelState::None(panel_state) => write_uniforms!(uniform::None {
|
||||
PanelState::None(panel_state) => Self::render_panel_none_geometry(
|
||||
wgpu,
|
||||
render_pass,
|
||||
pos_matrix,
|
||||
background_color: uniform::Vec4(panel_state.background_color),
|
||||
}),
|
||||
PanelState::Fade(panel_state) => {
|
||||
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)
|
||||
},
|
||||
};
|
||||
geometry_uniforms
|
||||
.none(wgpu, &shared.none.geometry_uniforms_bind_group_layout)
|
||||
.await,
|
||||
panel_state,
|
||||
)
|
||||
.into(),
|
||||
PanelState::Fade(panel_state) => Self::render_panel_fade_geometry(
|
||||
wgpu,
|
||||
shared,
|
||||
display_geometry,
|
||||
render_pass,
|
||||
pos_matrix,
|
||||
&mut geometry_uniforms.fade,
|
||||
panel_state,
|
||||
)
|
||||
.await
|
||||
.into(),
|
||||
PanelState::Slide(panel_state) => Self::render_panel_slide_geometry(
|
||||
wgpu,
|
||||
render_pass,
|
||||
pos_matrix,
|
||||
geometry_uniforms
|
||||
.slide(wgpu, &shared.slide.geometry_uniforms_bind_group_layout)
|
||||
.await,
|
||||
panel_state,
|
||||
)
|
||||
.into(),
|
||||
}
|
||||
}
|
||||
|
||||
let ratio = display_geometry.image_ratio(size);
|
||||
PanelFadeImageUniforms::new(ratio, swap_dir)
|
||||
};
|
||||
/// Renders a panel none's geometry
|
||||
fn render_panel_none_geometry(
|
||||
wgpu: &Wgpu,
|
||||
render_pass: &mut wgpu::RenderPass<'_>,
|
||||
pos_matrix: uniform::Matrix4x4,
|
||||
geometry_uniforms: &PanelGeometryNoneUniforms,
|
||||
panel_state: &PanelNoneState,
|
||||
) -> metrics::RenderPanelGeometryNoneFrameTime {
|
||||
#[time(write_uniforms)]
|
||||
let () = Self::write_uniforms(wgpu, &geometry_uniforms.buffer, uniform::None {
|
||||
pos_matrix,
|
||||
background_color: uniform::Vec4(panel_state.background_color),
|
||||
});
|
||||
|
||||
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());
|
||||
// Bind the geometry uniforms
|
||||
render_pass.set_bind_group(0, &geometry_uniforms.bind_group, &[]);
|
||||
|
||||
let fade_duration = panel_state.fade_duration_norm();
|
||||
let progress = panel_state.progress_norm();
|
||||
match panel_state.shader() {
|
||||
PanelFadeShader::Basic => write_uniforms!(uniform::Fade {
|
||||
#[time(draw)]
|
||||
render_pass.draw_indexed(0..6, 0, 0..1);
|
||||
|
||||
metrics::RenderPanelGeometryNoneFrameTime { write_uniforms, draw }
|
||||
}
|
||||
|
||||
async fn render_panel_fade_geometry(
|
||||
wgpu: &Wgpu,
|
||||
shared: &PanelsRendererShared,
|
||||
display_geometry: &DisplayGeometry,
|
||||
render_pass: &mut wgpu::RenderPass<'_>,
|
||||
pos_matrix: uniform::Matrix4x4,
|
||||
geometry_uniforms: &mut PanelGeometryFadeUniforms,
|
||||
panel_state: &PanelFadeState,
|
||||
) -> metrics::RenderPanelGeometryFadeFrameTime {
|
||||
let p = panel_state.progress_norm();
|
||||
let f = panel_state.fade_duration_norm();
|
||||
|
||||
// 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.image(wgpu, &shared.fade.geometry_uniforms_bind_group_layout, panel_image_slot);
|
||||
|
||||
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 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,
|
||||
};
|
||||
|
||||
// If the alpha is 0, we can skip this image
|
||||
if alpha == 0.0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
#[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,
|
||||
prev,
|
||||
cur,
|
||||
next,
|
||||
fade_duration,
|
||||
image_ratio: uniform::Vec2(image_ratio.into()),
|
||||
progress,
|
||||
_unused: [0; 2],
|
||||
alpha,
|
||||
mix_strength: strength * 4.0 * f32::max(alpha_cur * alpha_prev, alpha_cur * alpha_next),
|
||||
_unused: [0; _],
|
||||
}),
|
||||
PanelFadeShader::White { strength } => write_uniforms!(uniform::FadeWhite {
|
||||
PanelFadeShader::Out { strength } =>
|
||||
Self::write_uniforms(wgpu, &geometry_uniforms.buffer, uniform::FadeOut {
|
||||
pos_matrix,
|
||||
prev,
|
||||
cur,
|
||||
next,
|
||||
fade_duration,
|
||||
image_ratio: uniform::Vec2(image_ratio.into()),
|
||||
progress,
|
||||
alpha,
|
||||
strength,
|
||||
_unused: 0,
|
||||
_unused: [0; _],
|
||||
}),
|
||||
PanelFadeShader::Out { strength } => write_uniforms!(uniform::FadeOut {
|
||||
PanelFadeShader::In { strength } =>
|
||||
Self::write_uniforms(wgpu, &geometry_uniforms.buffer, uniform::FadeIn {
|
||||
pos_matrix,
|
||||
prev,
|
||||
cur,
|
||||
next,
|
||||
fade_duration,
|
||||
image_ratio: uniform::Vec2(image_ratio.into()),
|
||||
progress,
|
||||
alpha,
|
||||
strength,
|
||||
_unused: 0,
|
||||
_unused: [0; _],
|
||||
}),
|
||||
PanelFadeShader::In { strength } => write_uniforms!(uniform::FadeIn {
|
||||
pos_matrix,
|
||||
prev,
|
||||
cur,
|
||||
next,
|
||||
fade_duration,
|
||||
progress,
|
||||
strength,
|
||||
_unused: 0,
|
||||
}),
|
||||
}
|
||||
},
|
||||
PanelState::Slide(_panel_state) => write_uniforms!(uniform::Slide { pos_matrix }),
|
||||
};
|
||||
|
||||
// Bind the geometry uniforms
|
||||
render_pass.set_bind_group(0, &geometry_uniforms.bind_group, &[]);
|
||||
|
||||
// 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,
|
||||
});
|
||||
}
|
||||
|
||||
render_pass.set_bind_group(0, &geometry_uniforms.bind_group, &[]);
|
||||
metrics::RenderPanelGeometryFadeFrameTime { images: image_metrics }
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates the panel geometry uniforms
|
||||
fn create_geometry_uniforms(wgpu: &Wgpu, layout: &wgpu::BindGroupLayout) -> PanelGeometryUniforms {
|
||||
// Create the uniforms
|
||||
let buffer_descriptor = wgpu::BufferDescriptor {
|
||||
label: Some("zsw-panel-geometry-uniforms-buffer"),
|
||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||
size: u64::try_from(MAX_UNIFORM_SIZE).expect("Maximum uniform size didn't fit into a `u64`"),
|
||||
mapped_at_creation: false,
|
||||
};
|
||||
let buffer = wgpu.device.create_buffer(&buffer_descriptor);
|
||||
/// Renders a panel slide's geometry
|
||||
fn render_panel_slide_geometry(
|
||||
wgpu: &Wgpu,
|
||||
render_pass: &mut wgpu::RenderPass<'_>,
|
||||
pos_matrix: uniform::Matrix4x4,
|
||||
geometry_uniforms: &PanelGeometrySlideUniforms,
|
||||
_panel_state: &PanelSlideState,
|
||||
) -> metrics::RenderPanelGeometrySlideFrameTime {
|
||||
#[time(write_uniforms)]
|
||||
let () = Self::write_uniforms(wgpu, &geometry_uniforms.buffer, uniform::Slide { pos_matrix });
|
||||
|
||||
// Create the uniform bind group
|
||||
let bind_group_descriptor = wgpu::BindGroupDescriptor {
|
||||
label: Some("zsw-panel-geometry-uniforms-bind-group"),
|
||||
layout,
|
||||
entries: &[wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: buffer.as_entire_binding(),
|
||||
}],
|
||||
};
|
||||
let bind_group = wgpu.device.create_bind_group(&bind_group_descriptor);
|
||||
// Bind the geometry uniforms
|
||||
render_pass.set_bind_group(0, &geometry_uniforms.bind_group, &[]);
|
||||
|
||||
PanelGeometryUniforms { buffer, bind_group }
|
||||
#[time(draw)]
|
||||
render_pass.draw_indexed(0..6, 0, 0..1);
|
||||
|
||||
metrics::RenderPanelGeometrySlideFrameTime { write_uniforms, draw }
|
||||
}
|
||||
|
||||
/// Writes `uniforms` into `buffer`.
|
||||
fn write_uniforms<T>(wgpu: &Wgpu, buffer: &wgpu::Buffer, uniforms: T)
|
||||
where
|
||||
T: bytemuck::NoUninit,
|
||||
{
|
||||
wgpu.queue.write_buffer(buffer, 0, bytemuck::bytes_of(&uniforms));
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates the vertices
|
||||
@ -635,25 +775,6 @@ fn create_msaa_framebuffer(
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates the uniforms bind group layout
|
||||
fn create_uniforms_bind_group_layout(wgpu: &Wgpu) -> wgpu::BindGroupLayout {
|
||||
let descriptor = wgpu::BindGroupLayoutDescriptor {
|
||||
label: Some("zsw-panel-geometry-uniforms-bind-group-layout"),
|
||||
entries: &[wgpu::BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
|
||||
ty: wgpu::BindingType::Buffer {
|
||||
ty: wgpu::BufferBindingType::Uniform,
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: None,
|
||||
},
|
||||
count: None,
|
||||
}],
|
||||
};
|
||||
|
||||
wgpu.device.create_bind_group_layout(&descriptor)
|
||||
}
|
||||
|
||||
/// Shader
|
||||
#[derive(PartialEq, Clone, Copy, Debug)]
|
||||
pub enum PanelShader {
|
||||
|
||||
@ -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
|
||||
@ -120,14 +90,3 @@ pub struct FadeIn {
|
||||
pub struct Slide {
|
||||
pub pos_matrix: Matrix4x4,
|
||||
}
|
||||
|
||||
/// The maximum uniform size
|
||||
pub const MAX_UNIFORM_SIZE: usize = zsw_util::array_max(&[
|
||||
size_of::<None>(),
|
||||
size_of::<Fade>(),
|
||||
size_of::<FadeWhite>(),
|
||||
size_of::<FadeOut>(),
|
||||
size_of::<FadeIn>(),
|
||||
size_of::<Slide>(),
|
||||
])
|
||||
.expect("No max uniform size");
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -2,7 +2,10 @@
|
||||
|
||||
// Imports
|
||||
use {
|
||||
crate::playlist::PlaylistPlayer,
|
||||
crate::{
|
||||
panel::{geometry::PanelGeometryFadeImageUniforms, renderer::uniform},
|
||||
playlist::PlaylistPlayer,
|
||||
},
|
||||
app_error::Context,
|
||||
image::{DynamicImage, imageops},
|
||||
std::{self, mem, path::Path, sync::Arc},
|
||||
@ -13,13 +16,26 @@ use {
|
||||
};
|
||||
|
||||
/// Panel fade images shared
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Debug)]
|
||||
pub struct PanelFadeImagesShared {
|
||||
/// Geometry uniforms bind group layout
|
||||
pub geometry_uniforms_bind_group_layout: wgpu::BindGroupLayout,
|
||||
|
||||
/// Image bind group layout
|
||||
image_bind_group_layout: OnceCell<wgpu::BindGroupLayout>,
|
||||
pub image_bind_group_layout: OnceCell<wgpu::BindGroupLayout>,
|
||||
}
|
||||
|
||||
impl PanelFadeImagesShared {
|
||||
/// Creates the shared
|
||||
pub fn new(wgpu: &Wgpu) -> Self {
|
||||
let geometry_uniforms_bind_group_layout = self::create_geometry_uniforms_bind_group_layout(wgpu);
|
||||
|
||||
Self {
|
||||
geometry_uniforms_bind_group_layout,
|
||||
image_bind_group_layout: OnceCell::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the image bind group layout, or initializes it, if uninitialized
|
||||
pub async fn image_bind_group_layout(&self, wgpu: &Wgpu) -> &wgpu::BindGroupLayout {
|
||||
self.image_bind_group_layout
|
||||
@ -43,9 +59,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 +69,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 +79,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 +130,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 +149,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 +161,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 +192,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 +220,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 +283,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,
|
||||
@ -292,6 +320,25 @@ pub fn load(path: &Arc<Path>, max_image_size: u32) -> Result<DynamicImage, AppEr
|
||||
Ok(image)
|
||||
}
|
||||
|
||||
/// Creates the geometry uniforms bind group layout
|
||||
fn create_geometry_uniforms_bind_group_layout(wgpu: &Wgpu) -> wgpu::BindGroupLayout {
|
||||
let descriptor = wgpu::BindGroupLayoutDescriptor {
|
||||
label: Some("zsw-panel-fade-geometry-uniforms-bind-group-layout"),
|
||||
entries: &[wgpu::BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
|
||||
ty: wgpu::BindingType::Buffer {
|
||||
ty: wgpu::BufferBindingType::Uniform,
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: None,
|
||||
},
|
||||
count: None,
|
||||
}],
|
||||
};
|
||||
|
||||
wgpu.device.create_bind_group_layout(&descriptor)
|
||||
}
|
||||
|
||||
/// Creates the fade image bind group layout
|
||||
fn create_bind_group_layout(wgpu: &Wgpu) -> wgpu::BindGroupLayout {
|
||||
let descriptor = wgpu::BindGroupLayoutDescriptor {
|
||||
@ -310,26 +357,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 +370,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 +379,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),
|
||||
},
|
||||
],
|
||||
@ -373,6 +390,40 @@ fn create_image_bind_group(
|
||||
wgpu.device.create_bind_group(&descriptor)
|
||||
}
|
||||
|
||||
/// Creates the image geometry uniforms
|
||||
pub fn create_image_geometry_uniforms(wgpu: &Wgpu, layout: &wgpu::BindGroupLayout) -> PanelGeometryFadeImageUniforms {
|
||||
// Create the uniforms
|
||||
let buffer_descriptor = wgpu::BufferDescriptor {
|
||||
label: Some("zsw-panel-fade-geometry-uniforms-buffer"),
|
||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||
size: u64::try_from(
|
||||
zsw_util::array_max(&[
|
||||
size_of::<uniform::FadeBasic>(),
|
||||
size_of::<uniform::FadeWhite>(),
|
||||
size_of::<uniform::FadeOut>(),
|
||||
size_of::<uniform::FadeIn>(),
|
||||
])
|
||||
.expect("No max uniform size"),
|
||||
)
|
||||
.expect("Maximum uniform size didn't fit into a `u64`"),
|
||||
mapped_at_creation: false,
|
||||
};
|
||||
let buffer = wgpu.device.create_buffer(&buffer_descriptor);
|
||||
|
||||
// Create the uniform bind group
|
||||
let bind_group_descriptor = wgpu::BindGroupDescriptor {
|
||||
label: Some("zsw-panel-fade-geometry-uniforms-bind-group"),
|
||||
layout,
|
||||
entries: &[wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: buffer.as_entire_binding(),
|
||||
}],
|
||||
};
|
||||
let bind_group = wgpu.device.create_bind_group(&bind_group_descriptor);
|
||||
|
||||
PanelGeometryFadeImageUniforms { buffer, bind_group }
|
||||
}
|
||||
|
||||
/// Creates the image sampler
|
||||
fn create_image_sampler(wgpu: &Wgpu) -> wgpu::Sampler {
|
||||
let descriptor = wgpu::SamplerDescriptor {
|
||||
|
||||
@ -1,5 +1,11 @@
|
||||
//! Panel none state
|
||||
|
||||
// Imports
|
||||
use {
|
||||
crate::panel::{geometry::PanelGeometryNoneUniforms, renderer::uniform},
|
||||
zsw_wgpu::Wgpu,
|
||||
};
|
||||
|
||||
/// Panel none state
|
||||
#[derive(Debug)]
|
||||
pub struct PanelNoneState {
|
||||
@ -13,3 +19,68 @@ impl PanelNoneState {
|
||||
Self { background_color }
|
||||
}
|
||||
}
|
||||
|
||||
/// Panel none images shared
|
||||
#[derive(Debug)]
|
||||
pub struct PanelNoneImagesShared {
|
||||
/// Geometry uniforms bind group layout
|
||||
pub geometry_uniforms_bind_group_layout: wgpu::BindGroupLayout,
|
||||
}
|
||||
|
||||
impl PanelNoneImagesShared {
|
||||
/// Creates the shared
|
||||
pub fn new(wgpu: &Wgpu) -> Self {
|
||||
let geometry_uniforms_bind_group_layout = self::create_geometry_uniforms_bind_group_layout(wgpu);
|
||||
|
||||
Self {
|
||||
geometry_uniforms_bind_group_layout,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates the geometry uniforms bind group layout
|
||||
fn create_geometry_uniforms_bind_group_layout(wgpu: &Wgpu) -> wgpu::BindGroupLayout {
|
||||
let descriptor = wgpu::BindGroupLayoutDescriptor {
|
||||
label: Some("zsw-panel-none-geometry-uniforms-bind-group-layout"),
|
||||
entries: &[wgpu::BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
|
||||
ty: wgpu::BindingType::Buffer {
|
||||
ty: wgpu::BufferBindingType::Uniform,
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: None,
|
||||
},
|
||||
count: None,
|
||||
}],
|
||||
};
|
||||
|
||||
wgpu.device.create_bind_group_layout(&descriptor)
|
||||
}
|
||||
|
||||
/// Creates the panel none geometry uniforms
|
||||
pub fn create_geometry_uniforms(wgpu: &Wgpu, layout: &wgpu::BindGroupLayout) -> PanelGeometryNoneUniforms {
|
||||
// Create the uniforms
|
||||
let buffer_descriptor = wgpu::BufferDescriptor {
|
||||
label: Some("zsw-panel-none-geometry-uniforms-buffer"),
|
||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||
size: u64::try_from(
|
||||
zsw_util::array_max(&[size_of::<uniform::None>()]).expect("No max uniform size"),
|
||||
)
|
||||
.expect("Maximum uniform size didn't fit into a `u64`"),
|
||||
mapped_at_creation: false,
|
||||
};
|
||||
let buffer = wgpu.device.create_buffer(&buffer_descriptor);
|
||||
|
||||
// Create the uniform bind group
|
||||
let bind_group_descriptor = wgpu::BindGroupDescriptor {
|
||||
label: Some("zsw-panel-none-geometry-uniforms-bind-group"),
|
||||
layout,
|
||||
entries: &[wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: buffer.as_entire_binding(),
|
||||
}],
|
||||
};
|
||||
let bind_group = wgpu.device.create_bind_group(&bind_group_descriptor);
|
||||
|
||||
PanelGeometryNoneUniforms { buffer, bind_group }
|
||||
}
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
//! Panel slide state
|
||||
|
||||
use crate::panel::PanelSlideShader;
|
||||
// Imports
|
||||
use {
|
||||
crate::panel::{PanelSlideShader, geometry::PanelGeometrySlideUniforms, renderer::uniform},
|
||||
zsw_wgpu::Wgpu,
|
||||
};
|
||||
|
||||
/// Panel slide state
|
||||
#[derive(Debug)]
|
||||
@ -25,3 +29,68 @@ impl PanelSlideState {
|
||||
&mut self.shader
|
||||
}
|
||||
}
|
||||
|
||||
/// Panel slide images shared
|
||||
#[derive(Debug)]
|
||||
pub struct PanelSlideImagesShared {
|
||||
/// Geometry uniforms bind group layout
|
||||
pub geometry_uniforms_bind_group_layout: wgpu::BindGroupLayout,
|
||||
}
|
||||
|
||||
impl PanelSlideImagesShared {
|
||||
/// Creates the shared
|
||||
pub fn new(wgpu: &Wgpu) -> Self {
|
||||
let geometry_uniforms_bind_group_layout = self::create_geometry_uniforms_bind_group_layout(wgpu);
|
||||
|
||||
Self {
|
||||
geometry_uniforms_bind_group_layout,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates the geometry uniforms bind group layout
|
||||
fn create_geometry_uniforms_bind_group_layout(wgpu: &Wgpu) -> wgpu::BindGroupLayout {
|
||||
let descriptor = wgpu::BindGroupLayoutDescriptor {
|
||||
label: Some("zsw-panel-slide-geometry-uniforms-bind-group-layout"),
|
||||
entries: &[wgpu::BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
|
||||
ty: wgpu::BindingType::Buffer {
|
||||
ty: wgpu::BufferBindingType::Uniform,
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: None,
|
||||
},
|
||||
count: None,
|
||||
}],
|
||||
};
|
||||
|
||||
wgpu.device.create_bind_group_layout(&descriptor)
|
||||
}
|
||||
|
||||
/// Creates the panel none geometry uniforms
|
||||
pub fn create_geometry_uniforms(wgpu: &Wgpu, layout: &wgpu::BindGroupLayout) -> PanelGeometrySlideUniforms {
|
||||
// Create the uniforms
|
||||
let buffer_descriptor = wgpu::BufferDescriptor {
|
||||
label: Some("zsw-panel-none-geometry-uniforms-buffer"),
|
||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||
size: u64::try_from(
|
||||
zsw_util::array_max(&[size_of::<uniform::Slide>()]).expect("No max uniform size"),
|
||||
)
|
||||
.expect("Maximum uniform size didn't fit into a `u64`"),
|
||||
mapped_at_creation: false,
|
||||
};
|
||||
let buffer = wgpu.device.create_buffer(&buffer_descriptor);
|
||||
|
||||
// Create the uniform bind group
|
||||
let bind_group_descriptor = wgpu::BindGroupDescriptor {
|
||||
label: Some("zsw-panel-none-geometry-uniforms-bind-group"),
|
||||
layout,
|
||||
entries: &[wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: buffer.as_entire_binding(),
|
||||
}],
|
||||
};
|
||||
let bind_group = wgpu.device.create_bind_group(&bind_group_descriptor);
|
||||
|
||||
PanelGeometrySlideUniforms { buffer, bind_group }
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user