Add retained paints and shared text selection
This commit is contained in:
1 parent
1e6d3b1edd
commit
a33fbca966
42 files changed
+2424
-470
No files matched your search
@@ -0,0 +1,178 @@
|
||||
#![recursion_limit = "256"]
|
||||
|
||||
use iris::{harness::Harness, prelude::*};
|
||||
use pollster::FutureExt;
|
||||
use std::sync::OnceLock;
|
||||
use wgpu::TextureFormat;
|
||||
|
||||
const SIZE: Vec2 = Vec2::new(2.0, 1.0);
|
||||
const SOLID: Srgba8 = Srgba8::rgb(17, 127, 231);
|
||||
const CHANGED_SOLID: Srgba8 = Srgba8::rgb(243, 139, 168);
|
||||
const IMAGE: Srgba8 = Srgba8::rgb(205, 214, 244);
|
||||
|
||||
/// Covers the whole colour path rather than a conversion helper: an sRGB
|
||||
/// literal enters the linear paint buffer, an sRGB image is sampled as
|
||||
/// linear, the Iris shader returns both, and the sRGB attachment encodes the
|
||||
/// stored bytes. Replacing only the paint-table entry also proves that a
|
||||
/// theme change reaches an already-retained primitive.
|
||||
#[test]
|
||||
fn solid_paints_and_images_round_trip_through_an_srgb_target() {
|
||||
let gpu = Gpu::open();
|
||||
let mut harness = Harness::new(SIZE, 1.0);
|
||||
let solid = harness.rsc.ui.paints.add(SOLID);
|
||||
let bitmap = image::DynamicImage::ImageRgba8(image::RgbaImage::from_pixel(
|
||||
1,
|
||||
1,
|
||||
image::Rgba([IMAGE.r, IMAGE.g, IMAGE.b, IMAGE.a]),
|
||||
));
|
||||
let bitmap = image::<iris::harness::HarnessRsc>(bitmap)(&mut harness.rsc);
|
||||
let root = (rect(solid.clone()).sized((1, 1)), bitmap)
|
||||
.span(Dir::RIGHT)
|
||||
.add_strong(&mut harness.rsc)
|
||||
.any();
|
||||
harness.state.set_root(root);
|
||||
harness.frame(0);
|
||||
|
||||
let mut renderer =
|
||||
UiRenderNode::new(&gpu.device, &gpu.queue, TextureFormat::Rgba8UnormSrgb, SIZE)
|
||||
.expect("the Iris pipeline should accept an sRGB render target");
|
||||
let first = render(&gpu, &mut renderer, &mut harness);
|
||||
assert_pixel(first[0], SOLID, "linear paint buffer -> sRGB attachment");
|
||||
assert_pixel(first[1], IMAGE, "sRGB texture -> shader -> sRGB attachment");
|
||||
|
||||
harness.rsc.ui.paints.set(&solid, CHANGED_SOLID);
|
||||
let changed = render(&gpu, &mut renderer, &mut harness);
|
||||
assert_pixel(
|
||||
changed[0],
|
||||
CHANGED_SOLID,
|
||||
"updated paint table -> retained primitive",
|
||||
);
|
||||
assert_pixel(changed[1], IMAGE, "unchanged image after paint update");
|
||||
}
|
||||
|
||||
fn render(gpu: &Gpu, renderer: &mut UiRenderNode, harness: &mut Harness) -> [[u8; 4]; 2] {
|
||||
renderer.update(
|
||||
&gpu.device,
|
||||
&gpu.queue,
|
||||
&mut harness.rsc.ui,
|
||||
&mut harness.render,
|
||||
);
|
||||
let texture = gpu.device.create_texture(&wgpu::TextureDescriptor {
|
||||
label: Some("Iris colour-space target"),
|
||||
size: wgpu::Extent3d {
|
||||
width: 2,
|
||||
height: 1,
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: wgpu::TextureFormat::Rgba8UnormSrgb,
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
|
||||
view_formats: &[],
|
||||
});
|
||||
let view = texture.create_view(&Default::default());
|
||||
let readback = gpu.device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: Some("Iris colour-space readback"),
|
||||
size: 256,
|
||||
usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
|
||||
mapped_at_creation: false,
|
||||
});
|
||||
let mut encoder = gpu.device.create_command_encoder(&Default::default());
|
||||
{
|
||||
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: Some("Iris colour-space pass"),
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view: &view,
|
||||
depth_slice: None,
|
||||
resolve_target: None,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(LinearRgba::BLACK.to_wgpu()),
|
||||
store: wgpu::StoreOp::Store,
|
||||
},
|
||||
})],
|
||||
depth_stencil_attachment: None,
|
||||
timestamp_writes: None,
|
||||
occlusion_query_set: None,
|
||||
multiview_mask: None,
|
||||
});
|
||||
renderer.draw(&mut pass);
|
||||
}
|
||||
encoder.copy_texture_to_buffer(
|
||||
texture.as_image_copy(),
|
||||
wgpu::TexelCopyBufferInfo {
|
||||
buffer: &readback,
|
||||
layout: wgpu::TexelCopyBufferLayout {
|
||||
offset: 0,
|
||||
bytes_per_row: Some(256),
|
||||
rows_per_image: Some(1),
|
||||
},
|
||||
},
|
||||
wgpu::Extent3d {
|
||||
width: 2,
|
||||
height: 1,
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
);
|
||||
gpu.queue.submit([encoder.finish()]);
|
||||
|
||||
let slice = readback.slice(..);
|
||||
slice.map_async(wgpu::MapMode::Read, |result| {
|
||||
result.expect("mapping the colour-space readback")
|
||||
});
|
||||
gpu.device
|
||||
.poll(wgpu::PollType::wait_indefinitely())
|
||||
.expect("waiting for the colour-space readback");
|
||||
let mapped = slice
|
||||
.get_mapped_range()
|
||||
.expect("reading the mapped colour-space buffer");
|
||||
let pixels = [
|
||||
mapped[0..4].try_into().unwrap(),
|
||||
mapped[4..8].try_into().unwrap(),
|
||||
];
|
||||
drop(mapped);
|
||||
readback.unmap();
|
||||
pixels
|
||||
}
|
||||
|
||||
fn assert_pixel(got: [u8; 4], want: Srgba8, path: &str) {
|
||||
let want = [want.r, want.g, want.b, want.a];
|
||||
assert!(
|
||||
got.into_iter()
|
||||
.zip(want)
|
||||
.all(|(got, want)| got.abs_diff(want) <= 1),
|
||||
"{path}: stored {got:?}, expected {want:?} (within one code value)",
|
||||
);
|
||||
}
|
||||
|
||||
fn vulkan_instance() -> &'static wgpu::Instance {
|
||||
static INSTANCE: OnceLock<wgpu::Instance> = OnceLock::new();
|
||||
INSTANCE.get_or_init(wgpu::Instance::default)
|
||||
}
|
||||
|
||||
struct Gpu {
|
||||
device: wgpu::Device,
|
||||
queue: wgpu::Queue,
|
||||
}
|
||||
|
||||
impl Gpu {
|
||||
fn open() -> Self {
|
||||
let adapter = vulkan_instance()
|
||||
.request_adapter(&wgpu::RequestAdapterOptions::default())
|
||||
.block_on()
|
||||
.expect("no wgpu adapter, so Iris's colour-space pipeline went unchecked");
|
||||
let info = adapter.get_info();
|
||||
eprintln!(
|
||||
"color_space: {} ({:?}, {})",
|
||||
info.name, info.backend, info.driver
|
||||
);
|
||||
let (device, queue) = adapter
|
||||
.request_device(&wgpu::DeviceDescriptor {
|
||||
required_limits: iris_core::device_limits(),
|
||||
..Default::default()
|
||||
})
|
||||
.block_on()
|
||||
.expect("could not get a device from the adapter");
|
||||
Self { device, queue }
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user