use impeller_hal::{
Batch, BlendMode, Extent2D, Material, PassDescriptor, PixelFormat, TextureDescriptor, TileMode,
};
use impeller_hal_vulkan::{ContextConfig, DevicePreference, VulkanContext};
fn across_the_target(scale: f32) -> [f32; 12] {
[
scale, 0.0, 0.0, 0.0, 0.0, -scale, 0.0, 0.0, scale, scale, 1.0, 0.0,
]
}
const SIZE: Extent2D = Extent2D {
width: 16,
height: 16,
};
const FULL: [[f32; 2]; 4] = [[-1.0, -1.0], [1.0, -1.0], [1.0, 1.0], [-1.0, 1.0]];
const QUAD: [u32; 6] = [0, 1, 2, 0, 2, 3];
fn context() -> Option<VulkanContext> {
match VulkanContext::with_config(ContextConfig {
device: DevicePreference::Auto,
validation: true,
}) {
Ok(ctx) => Some(ctx),
Err(e) => {
eprintln!("skipping: no usable Vulkan device ({e})");
None
}
}
}
fn assert_clean(ctx: &VulkanContext, what: &str) {
if !ctx.validation_active() {
eprintln!("skipping validation assertions: no layer installed");
return;
}
let errors: Vec<_> = ctx
.validation_messages()
.into_iter()
.filter(|m| m.severity == impeller_hal_vulkan::ValidationSeverity::Error)
.collect();
assert!(errors.is_empty(), "{what}: validation errors: {errors:?}");
}
fn image_material() -> Material {
Material::Image {
to_local: across_the_target(0.5),
slot: 0,
alpha: 1.0,
tile: TileMode::Clamp,
sampling: impeller_hal::Sampling::Linear,
source: [0.0, 0.0, 1.0, 1.0],
tint: [1.0, 1.0, 1.0, 1.0],
}
}
#[test]
fn sampling_an_uploaded_texture_is_valid() {
let Some(mut ctx) = context() else { return };
let mut source = ctx
.create_texture(&TextureDescriptor::offscreen(
Extent2D::new(2, 2),
PixelFormat::Rgba8Unorm,
))
.expect("source");
ctx.write_texture(&mut source, &[255u8; 16])
.expect("upload");
let mut batch = Batch::new();
batch
.push(&FULL, &QUAD, image_material(), BlendMode::Src)
.expect("push");
let mut target = ctx
.create_texture(&TextureDescriptor::offscreen(SIZE, PixelFormat::Rgba8Unorm))
.expect("target");
ctx.submit_batch_textured(
&mut target,
&batch,
PassDescriptor::clear([0.0; 4]),
&[&source],
)
.expect("submit");
ctx.destroy_texture(target);
ctx.destroy_texture(source);
assert_clean(&ctx, "sampling an uploaded texture");
}
#[test]
fn sampling_a_rendered_target_is_valid() {
let Some(mut ctx) = context() else { return };
let mut layer = ctx
.create_texture(&TextureDescriptor::offscreen(SIZE, PixelFormat::Rgba8Unorm))
.expect("layer");
let mut fill = Batch::new();
fill.push(
&FULL,
&QUAD,
Material::solid([1.0, 0.0, 0.0, 1.0]),
BlendMode::Src,
)
.expect("push");
ctx.submit_batch(&mut layer, &fill, PassDescriptor::clear([0.0; 4]))
.expect("fill the layer");
let mut composite = Batch::new();
composite
.push(&FULL, &QUAD, image_material(), BlendMode::SrcOver)
.expect("push");
let mut target = ctx
.create_texture(&TextureDescriptor::offscreen(SIZE, PixelFormat::Rgba8Unorm))
.expect("target");
ctx.submit_batch_textured(
&mut target,
&composite,
PassDescriptor::clear([0.0; 4]),
&[&layer],
)
.expect("composite");
ctx.destroy_texture(target);
ctx.destroy_texture(layer);
assert_clean(&ctx, "sampling a rendered target");
}
#[test]
fn a_batch_that_samples_nothing_still_binds_a_valid_descriptor() {
let Some(mut ctx) = context() else { return };
let mut batch = Batch::new();
batch
.push(
&FULL,
&QUAD,
Material::solid([0.0, 1.0, 0.0, 1.0]),
BlendMode::Src,
)
.expect("push");
let mut target = ctx
.create_texture(&TextureDescriptor::offscreen(SIZE, PixelFormat::Rgba8Unorm))
.expect("target");
ctx.submit_batch(&mut target, &batch, PassDescriptor::clear([0.0; 4]))
.expect("submit");
ctx.destroy_texture(target);
assert_clean(&ctx, "a batch that samples nothing");
}
#[test]
fn sampling_several_textures_in_one_batch_is_valid() {
let Some(mut ctx) = context() else { return };
let mut textures = Vec::new();
for _ in 0..2 {
let mut texture = ctx
.create_texture(&TextureDescriptor::offscreen(
Extent2D::new(2, 2),
PixelFormat::Rgba8Unorm,
))
.expect("texture");
ctx.write_texture(&mut texture, &[255u8; 16])
.expect("upload");
textures.push(texture);
}
let mut batch = Batch::new();
for slot in 0..2u32 {
let mut material = image_material();
if let Material::Image { slot: s, .. } = &mut material {
*s = slot;
}
batch
.push(&FULL, &QUAD, material, BlendMode::SrcOver)
.expect("push");
}
batch
.push(
&FULL,
&QUAD,
Material::solid([0.0, 0.0, 1.0, 0.5]),
BlendMode::SrcOver,
)
.expect("push");
let mut target = ctx
.create_texture(&TextureDescriptor::offscreen(SIZE, PixelFormat::Rgba8Unorm))
.expect("target");
let refs: Vec<_> = textures.iter().collect();
ctx.submit_batch_textured(&mut target, &batch, PassDescriptor::clear([0.0; 4]), &refs)
.expect("submit");
ctx.destroy_texture(target);
for texture in textures {
ctx.destroy_texture(texture);
}
assert_clean(&ctx, "several textures in one batch");
}