El repo publico se habia quedado en julio: el README anunciaba vello 0.7 + wgpu 27 (dos bumps atras) y faltaban los crates del ultimo mes. - refresco con scripts/actualizar-standalone.py --llimphi (cargo check OK) - llegan llimphi-cpu, llimphi-hybrid, llimphi-glifos y llimphi-wire-escena: los cuatro caminos a pixeles con un solo compositor - MANUAL.md / SDD.md / LEEME.md sincronizados con el monorepo - README: versiones al dia, install por crates.io (cargo add llimphi), seccion "beyond the 2D widget kit" y el indice de crates que apuntaba al propio README ahora apunta a MANUAL.md §19
97 lines
4.0 KiB
Rust
97 lines
4.0 KiB
Rust
//! El selector de formato de swapchain, certificado **sin GPU**.
|
|
//!
|
|
//! `elegir_formato` es puro: recibe la lista de formatos que ofrece una surface
|
|
//! y el objetivo de color pedido, y devuelve con cuál configurar el swapchain.
|
|
//! Por eso se puede probar contra listas inventadas —incluidas las que ninguna
|
|
//! máquina de acá tiene, como un Vulkan con colorspace extendido— en vez de
|
|
//! esperar a tener el hardware delante (Regla 8 de `CLAUDE.md`: evidencia de
|
|
//! texto).
|
|
//!
|
|
//! Lo que estos tests fijan, en orden de importancia:
|
|
//!
|
|
//! 1. **Cero regresión**: sin pedir nada, la elección es la misma de siempre.
|
|
//! 2. **Nunca un formato sRGB de hardware**, que lavaría la imagen aplicando la
|
|
//! curva dos veces (la intermedia ya es lineal).
|
|
//! 3. **Degradación**, no fallo: pedir 10 bits donde no hay devuelve 8.
|
|
|
|
use llimphi_hal::{elegir_formato, ColorTarget};
|
|
use wgpu::TextureFormat as F;
|
|
|
|
/// Lo que ofrece un compositor Wayland de escritorio normal (Mesa/Vulkan).
|
|
const ESCRITORIO: &[F] = &[F::Bgra8Unorm, F::Bgra8UnormSrgb, F::Rgba8Unorm, F::Rgba8UnormSrgb];
|
|
|
|
/// Una surface con colorspace extendido: además de lo de siempre, 10 bits y
|
|
/// float. Ninguna máquina de este repo la lista hoy; el selector igual tiene
|
|
/// que saber qué hacer con ella.
|
|
const AMPLIA: &[F] = &[
|
|
F::Bgra8Unorm,
|
|
F::Rgba8Unorm,
|
|
F::Rgb10a2Unorm,
|
|
F::Rgba16Float,
|
|
];
|
|
|
|
#[test]
|
|
fn el_default_elige_lo_de_siempre() {
|
|
// El comportamiento histórico, palabra por palabra: el primer Bgra8Unorm o
|
|
// Rgba8Unorm no-sRGB de la lista.
|
|
assert_eq!(elegir_formato(ESCRITORIO, ColorTarget::Srgb8), Some(F::Bgra8Unorm));
|
|
assert_eq!(elegir_formato(AMPLIA, ColorTarget::Srgb8), Some(F::Bgra8Unorm));
|
|
// Y si el compositor sólo ofrece Rgba8 (orden invertido), ése.
|
|
assert_eq!(
|
|
elegir_formato(&[F::Rgba8UnormSrgb, F::Rgba8Unorm], ColorTarget::Srgb8),
|
|
Some(F::Rgba8Unorm)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn nunca_elige_un_formato_srgb_de_hardware() {
|
|
// Una surface que SÓLO ofrece sRGB: no hay elección válida, y devolver
|
|
// `None` es lo correcto — el caller cae al primero de la lista a sabiendas,
|
|
// en vez de que el selector bendiga un formato que lava la imagen.
|
|
assert_eq!(
|
|
elegir_formato(&[F::Bgra8UnormSrgb, F::Rgba8UnormSrgb], ColorTarget::Srgb8),
|
|
None
|
|
);
|
|
for objetivo in [ColorTarget::Srgb8, ColorTarget::Rgb10, ColorTarget::Float16] {
|
|
let elegido = elegir_formato(ESCRITORIO, objetivo).unwrap();
|
|
assert!(
|
|
!format!("{elegido:?}").ends_with("Srgb"),
|
|
"{objetivo:?} eligió {elegido:?}, que es sRGB de hardware"
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn diez_bits_cuando_se_puede_y_ocho_cuando_no() {
|
|
assert_eq!(elegir_formato(AMPLIA, ColorTarget::Rgb10), Some(F::Rgb10a2Unorm));
|
|
// El mismo pedido contra un escritorio normal degrada a 8 bits en vez de
|
|
// fallar: pedir precisión que no hay no debe dejar la app sin ventana.
|
|
assert_eq!(elegir_formato(ESCRITORIO, ColorTarget::Rgb10), Some(F::Bgra8Unorm));
|
|
}
|
|
|
|
#[test]
|
|
fn float16_degrada_por_toda_la_cadena() {
|
|
assert_eq!(elegir_formato(AMPLIA, ColorTarget::Float16), Some(F::Rgba16Float));
|
|
// Sin float pero con 10 bits: el escalón intermedio, no el piso.
|
|
assert_eq!(
|
|
elegir_formato(&[F::Bgra8Unorm, F::Rgb10a2Unorm], ColorTarget::Float16),
|
|
Some(F::Rgb10a2Unorm)
|
|
);
|
|
// Sin ninguno de los dos: el piso.
|
|
assert_eq!(elegir_formato(ESCRITORIO, ColorTarget::Float16), Some(F::Bgra8Unorm));
|
|
}
|
|
|
|
#[test]
|
|
fn una_surface_sin_formatos_no_paniquea() {
|
|
// `get_capabilities` devuelve la lista vacía cuando la surface es
|
|
// incompatible con el adapter. El selector no indexa: contesta `None` y el
|
|
// caller decide (dos de los tres call sites devuelven un error legible).
|
|
assert_eq!(elegir_formato(&[], ColorTarget::Srgb8), None);
|
|
assert_eq!(elegir_formato(&[], ColorTarget::Float16), None);
|
|
}
|
|
|
|
#[test]
|
|
fn el_default_del_enum_es_el_comportamiento_historico() {
|
|
assert_eq!(ColorTarget::default(), ColorTarget::Srgb8);
|
|
}
|