A container's draw asked it once per child by searching the list of children it had added so far, and four other per-child steps searched a list too, so one draw cost the square of its children: 70% of a 1,600-child redraw was those searches. A draw takes a DrawId and leaves it on every widget it asks about; one note per widget is enough because the handle a container holds a child by cannot be cloned. tests/children_cost.rs is the rig that shows it, and it is the only one here that varies width: 3.680 ms to 0.811 ms at 1,600 children, and flat per child. Beside it, the rest of the fourteenth sweep of #19: a mask's rectangle resolved once per fragment instead of once per instance, which takes the storage buffers out of the fragment stage and is 8.8x on a screenful of deeply nested clips; TextBuffer::shape copying its attrs before the check that would not need them, which allocated once per named-family text per frame; a should_panic test on a debug assertion that made cargo test --release fail; Fixed::div, reached only by its own test; Moves::remove re-uploading an array it cannot have changed; and two comments the branch itself falsified. docs/LAYOUT_LOG.md has all eight with their measurements, the five things looked at and left, and what was verified.
67 lines
2.5 KiB
Rust
67 lines
2.5 KiB
Rust
//! The adapter, the surface configuration and the target the GPU rigs share,
|
|
//! so no two of them can probe for a device or make a target in different
|
|
//! ways.
|
|
|
|
use wgpu::*;
|
|
|
|
/// An adapter on whatever this machine has, or `None` where there is none.
|
|
///
|
|
/// Probed rather than assumed: there may be no Vulkan adapter, and GL is what
|
|
/// is left when there is not.
|
|
///
|
|
/// The instance is leaked deliberately. A Vulkan loader may unload the driver
|
|
/// when the last one drops, which can fault as a thread that used it exits --
|
|
/// and every test runs on a spawned thread.
|
|
pub fn adapter() -> Option<Adapter> {
|
|
let all = Instance::new(InstanceDescriptor::new_without_display_handle());
|
|
let instance = match pollster::block_on(all.request_adapter(&RequestAdapterOptions::default()))
|
|
{
|
|
Ok(_) => all,
|
|
Err(_) => Instance::new(InstanceDescriptor {
|
|
backends: Backends::GL,
|
|
..InstanceDescriptor::new_without_display_handle()
|
|
}),
|
|
};
|
|
let instance: &'static Instance = Box::leak(Box::new(instance));
|
|
pollster::block_on(instance.request_adapter(&RequestAdapterOptions::default())).ok()
|
|
}
|
|
|
|
pub fn config(format: TextureFormat, size: u32) -> SurfaceConfiguration {
|
|
SurfaceConfiguration {
|
|
usage: TextureUsages::RENDER_ATTACHMENT,
|
|
format,
|
|
color_space: SurfaceColorSpace::Auto,
|
|
width: size,
|
|
height: size,
|
|
present_mode: PresentMode::Fifo,
|
|
desired_maximum_frame_latency: 2,
|
|
alpha_mode: CompositeAlphaMode::Auto,
|
|
view_formats: vec![],
|
|
}
|
|
}
|
|
|
|
/// A square colour target to draw a pass into. `copy` adds the usage a rig
|
|
/// that reads the pixels back needs; one that only times the pass does not.
|
|
// This module is compiled into each rig target separately, so a helper the
|
|
// ones that make no target of their own do not call is dead code there.
|
|
#[allow(dead_code)]
|
|
pub fn target(device: &Device, format: TextureFormat, size: u32, copy: bool) -> Texture {
|
|
device.create_texture(&TextureDescriptor {
|
|
label: Some("gpu rig target"),
|
|
size: Extent3d {
|
|
width: size,
|
|
height: size,
|
|
depth_or_array_layers: 1,
|
|
},
|
|
mip_level_count: 1,
|
|
sample_count: 1,
|
|
dimension: TextureDimension::D2,
|
|
format,
|
|
usage: match copy {
|
|
true => TextureUsages::RENDER_ATTACHMENT | TextureUsages::COPY_SRC,
|
|
false => TextureUsages::RENDER_ATTACHMENT,
|
|
},
|
|
view_formats: &[],
|
|
})
|
|
}
|