use crate::gl_region::GlRegion;
pub type BackendError = Box<dyn std::error::Error + Send + Sync + 'static>;
#[derive(Debug, thiserror::Error)]
pub enum BlurError {
#[error("failed to create the {stage} while preparing the blur")]
ResourceCreation {
stage: BlurStage,
#[source]
source: BackendError,
},
#[error("target format {format} is not a supported render target for the blur composite")]
UnsupportedTarget {
format: String,
},
#[error("the grab source could not be produced from the framebuffer for region {region}")]
GrabFailed {
region: GlRegion,
#[source]
source: BackendError,
},
#[error("the GL context does not support the blur backend: {detail}")]
UnsupportedContext {
detail: String,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BlurStage {
PingPongTexture,
DownsamplePipeline,
UpsamplePipeline,
CompositePipeline,
UniformBuffer,
BindGroup,
ShaderCompile,
ProgramLink,
Framebuffer,
VertexArray,
}
impl std::fmt::Display for BlurStage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let label = match self {
Self::PingPongTexture => "ping-pong scratch texture",
Self::DownsamplePipeline => "downsample pipeline",
Self::UpsamplePipeline => "upsample pipeline",
Self::CompositePipeline => "composite pipeline",
Self::UniformBuffer => "uniform buffer",
Self::BindGroup => "bind group",
Self::ShaderCompile => "shader",
Self::ProgramLink => "shader program",
Self::Framebuffer => "framebuffer",
Self::VertexArray => "vertex array",
};
f.write_str(label)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::geometry::Scale;
#[test]
fn resource_creation_display_names_the_stage() {
let err = BlurError::ResourceCreation {
stage: BlurStage::CompositePipeline,
source: "device lost".into(),
};
assert_eq!(
err.to_string(),
"failed to create the composite pipeline while preparing the blur"
);
}
#[test]
fn unsupported_target_display_includes_the_format() {
let err = BlurError::UnsupportedTarget {
format: "Rgba8Snorm".to_owned(),
};
assert!(err.to_string().contains("Rgba8Snorm"));
}
#[test]
fn error_source_chains_to_the_backend_error() {
let err = BlurError::ResourceCreation {
stage: BlurStage::PingPongTexture,
source: "out of memory".into(),
};
let source = std::error::Error::source(&err).expect("a backend source is attached");
assert_eq!(source.to_string(), "out of memory");
}
#[test]
fn grab_failed_display_includes_the_region() {
let err = BlurError::GrabFailed {
region: GlRegion::from_bottom_px([0, 0], [10, 10], Scale::default()),
source: "no framebuffer".into(),
};
assert!(err.to_string().contains("region"));
assert!(err.to_string().contains("origin-bl"));
}
#[test]
fn stage_display_covers_every_variant() {
for stage in [
BlurStage::PingPongTexture,
BlurStage::DownsamplePipeline,
BlurStage::UpsamplePipeline,
BlurStage::CompositePipeline,
BlurStage::UniformBuffer,
BlurStage::BindGroup,
BlurStage::ShaderCompile,
BlurStage::ProgramLink,
BlurStage::Framebuffer,
BlurStage::VertexArray,
] {
assert!(!stage.to_string().is_empty());
}
}
#[test]
fn unsupported_context_display_includes_the_detail() {
let err = BlurError::UnsupportedContext {
detail: "requires GL 3.3 / GLES 3.0, found GL 2.1".to_owned(),
};
assert!(
err.to_string()
.contains("requires GL 3.3 / GLES 3.0, found GL 2.1")
);
}
}