compile bench mark for aliasing
This commit is contained in:
parent
afb322fa93
commit
33b42533ef
|
|
@ -1,17 +1,22 @@
|
||||||
//! These graph shapes compare command-level batching with render-pass span
|
//! These graph shapes compare command-level batching with render-pass span
|
||||||
//! contraction and barrier hoisting. Each has two disjoint buffer access sets
|
//! contraction and barrier hoisting. Each has two disjoint buffer access sets
|
||||||
//! joined by a shared-buffer dependency across the passes.
|
//! joined by a shared-buffer dependency across the passes. The resolve benchmarks
|
||||||
|
//! include device-dependent placement, alias-barrier rebuilding, and pass hoisting
|
||||||
|
//! in the timed region.
|
||||||
|
|
||||||
use std::{hint::black_box, time::Duration};
|
use std::{hint::black_box, sync::Arc, time::Duration};
|
||||||
|
|
||||||
use ash::vk;
|
use ash::vk;
|
||||||
use criterion::{BatchSize, Criterion, criterion_group, criterion_main};
|
use criterion::{BatchSize, Criterion, criterion_group, criterion_main};
|
||||||
|
use renderer_device::device::{MemoryLocation, QueueFlags};
|
||||||
|
use renderer_device::testing::TestVulkan;
|
||||||
use renderer_rendergraph::{
|
use renderer_rendergraph::{
|
||||||
Buffer, BufferRange, GraphBufferDesc, RenderGraph,
|
Buffer, BufferRange, GraphBufferDesc, RenderGraph,
|
||||||
commands::{BeginRendering, Command, EndRendering},
|
commands::{BeginRendering, Command, EndRendering},
|
||||||
recorder::{CommandIndex, CommandRecorder, RecordError, RecordedCommand, SideEffectMap},
|
recorder::{CommandIndex, CommandRecorder, RecordError, RecordedCommand, SideEffectMap},
|
||||||
resources::{SubresourceScope, SubresourceScopeInner},
|
resources::{ResourceState, SubresourceScope, SubresourceScopeInner},
|
||||||
};
|
};
|
||||||
|
use renderer_resources::{buffers::Buffer as DeviceBuffer, buffers::BufferDesc};
|
||||||
|
|
||||||
struct Use {
|
struct Use {
|
||||||
buffer: Buffer,
|
buffer: Buffer,
|
||||||
|
|
@ -46,6 +51,37 @@ impl RecordedCommand for Use {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct UseWithBridge {
|
||||||
|
buffer: Buffer,
|
||||||
|
bridge: Buffer,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Command for UseWithBridge {
|
||||||
|
fn side_effects(&self, command_index: CommandIndex, map: &mut SideEffectMap) {
|
||||||
|
for buffer in [self.buffer, self.bridge] {
|
||||||
|
map.insert(
|
||||||
|
command_index,
|
||||||
|
buffer.id(),
|
||||||
|
SubresourceScope {
|
||||||
|
stage: vk::PipelineStageFlags2::COMPUTE_SHADER,
|
||||||
|
access: vk::AccessFlags2::SHADER_WRITE,
|
||||||
|
subresource: SubresourceScopeInner::Buffer(BufferRange::full()),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RecordedCommand for UseWithBridge {
|
||||||
|
fn allowed_in_render_pass(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply(&mut self, _recorder: &mut CommandRecorder<'_>) -> Result<(), RecordError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn build(n_groups: usize, per: usize, pass: bool) -> RenderGraph {
|
fn build(n_groups: usize, per: usize, pass: bool) -> RenderGraph {
|
||||||
let mut graph = RenderGraph::new();
|
let mut graph = RenderGraph::new();
|
||||||
let a: Vec<Buffer> = (0..n_groups)
|
let a: Vec<Buffer> = (0..n_groups)
|
||||||
|
|
@ -113,6 +149,120 @@ fn build(n_groups: usize, per: usize, pass: bool) -> RenderGraph {
|
||||||
graph
|
graph
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn build_overlapping(n_groups: usize, per: usize) -> RenderGraph {
|
||||||
|
let mut graph = RenderGraph::new();
|
||||||
|
let buffers: Vec<Buffer> = (0..n_groups)
|
||||||
|
.map(|_| {
|
||||||
|
graph.create_buffer(GraphBufferDesc {
|
||||||
|
size: 4096,
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let shared = graph.create_buffer(GraphBufferDesc {
|
||||||
|
size: 4096,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
graph.push(BeginRendering {
|
||||||
|
color: vec![],
|
||||||
|
depth: None,
|
||||||
|
});
|
||||||
|
for _ in 0..per {
|
||||||
|
for &buffer in &buffers {
|
||||||
|
graph.push(Use {
|
||||||
|
buffer,
|
||||||
|
write: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
graph.push(Use {
|
||||||
|
buffer: shared,
|
||||||
|
write: true,
|
||||||
|
});
|
||||||
|
graph.push(EndRendering);
|
||||||
|
|
||||||
|
graph.push(BeginRendering {
|
||||||
|
color: vec![],
|
||||||
|
depth: None,
|
||||||
|
});
|
||||||
|
for _ in 0..per {
|
||||||
|
for &buffer in &buffers {
|
||||||
|
graph.push(Use {
|
||||||
|
buffer,
|
||||||
|
write: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
graph.push(Use {
|
||||||
|
buffer: shared,
|
||||||
|
write: false,
|
||||||
|
});
|
||||||
|
graph.push(EndRendering);
|
||||||
|
|
||||||
|
graph
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_aliasing(
|
||||||
|
n_groups: usize,
|
||||||
|
per: usize,
|
||||||
|
bridge: &Arc<DeviceBuffer>,
|
||||||
|
) -> RenderGraph {
|
||||||
|
let mut graph = RenderGraph::new();
|
||||||
|
let a: Vec<Buffer> = (0..n_groups)
|
||||||
|
.map(|_| {
|
||||||
|
graph.create_buffer(GraphBufferDesc {
|
||||||
|
size: 4096,
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let b: Vec<Buffer> = (0..n_groups)
|
||||||
|
.map(|_| {
|
||||||
|
graph.create_buffer(GraphBufferDesc {
|
||||||
|
size: 4096,
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let bridge = graph
|
||||||
|
.import_buffer(bridge.clone(), ResourceState::default())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Keep the first set outside the pass; render-pass resources interfere with
|
||||||
|
// each other by design and otherwise prevent placement reuse.
|
||||||
|
for _ in 0..per {
|
||||||
|
for &buffer in &a {
|
||||||
|
graph.push(Use {
|
||||||
|
buffer,
|
||||||
|
write: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for &buffer in &a {
|
||||||
|
graph.push(UseWithBridge { buffer, bridge });
|
||||||
|
}
|
||||||
|
|
||||||
|
graph.push(BeginRendering {
|
||||||
|
color: vec![],
|
||||||
|
depth: None,
|
||||||
|
});
|
||||||
|
for &buffer in &b {
|
||||||
|
graph.push(UseWithBridge { buffer, bridge });
|
||||||
|
}
|
||||||
|
for _ in 0..per {
|
||||||
|
for &buffer in &b {
|
||||||
|
graph.push(Use {
|
||||||
|
buffer,
|
||||||
|
write: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
graph.push(EndRendering);
|
||||||
|
|
||||||
|
graph
|
||||||
|
}
|
||||||
|
|
||||||
fn compile_benchmarks(criterion: &mut Criterion) {
|
fn compile_benchmarks(criterion: &mut Criterion) {
|
||||||
let mut group = criterion.benchmark_group("compile");
|
let mut group = criterion.benchmark_group("compile");
|
||||||
group
|
group
|
||||||
|
|
@ -137,5 +287,95 @@ fn compile_benchmarks(criterion: &mut Criterion) {
|
||||||
group.finish();
|
group.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
criterion_group!(benches, compile_benchmarks);
|
fn resolve_benchmarks(criterion: &mut Criterion, device: &renderer_device::Device) {
|
||||||
|
let bridge = Arc::new(
|
||||||
|
DeviceBuffer::new(
|
||||||
|
device.clone(),
|
||||||
|
BufferDesc {
|
||||||
|
size: 4096,
|
||||||
|
usage: vk::BufferUsageFlags::STORAGE_BUFFER,
|
||||||
|
queue_families: QueueFlags::empty(),
|
||||||
|
mem_location: MemoryLocation::GpuOnly,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.expect("create alias benchmark bridge buffer"),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut group = criterion.benchmark_group("compile_resolve");
|
||||||
|
group
|
||||||
|
.measurement_time(Duration::from_secs(4))
|
||||||
|
.sample_size(20);
|
||||||
|
|
||||||
|
for (name, overlapping) in [("alias", false), ("no_alias", true)] {
|
||||||
|
for per in [20, 50, 100] {
|
||||||
|
let graph = if overlapping {
|
||||||
|
build_overlapping(50, per)
|
||||||
|
} else {
|
||||||
|
build_aliasing(50, per, &bridge)
|
||||||
|
};
|
||||||
|
let mut probe = graph.compile().unwrap();
|
||||||
|
probe.resolve(device).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
probe
|
||||||
|
.resource_lifetimes()
|
||||||
|
.values()
|
||||||
|
.any(|lifetime| lifetime.placement.aliased),
|
||||||
|
!overlapping,
|
||||||
|
"unexpected placement aliasing in {name}/per_{per}"
|
||||||
|
);
|
||||||
|
let alias_barriers = probe
|
||||||
|
.barriers()
|
||||||
|
.iter()
|
||||||
|
.flat_map(|barrier| &barrier.resources)
|
||||||
|
.filter(|resource| {
|
||||||
|
probe
|
||||||
|
.resource_lifetimes()
|
||||||
|
.get(&resource.id)
|
||||||
|
.is_some_and(|lifetime| lifetime.placement.aliased)
|
||||||
|
&& resource.origin != renderer_rendergraph::BarrierOrigin::Initial
|
||||||
|
})
|
||||||
|
.count();
|
||||||
|
assert_eq!(
|
||||||
|
alias_barriers > 0,
|
||||||
|
!overlapping,
|
||||||
|
"unexpected alias barriers in {name}/per_{per}"
|
||||||
|
);
|
||||||
|
|
||||||
|
group.bench_function(format!("{name}/per_{per}"), |bencher| {
|
||||||
|
bencher.iter_batched(
|
||||||
|
|| {
|
||||||
|
if overlapping {
|
||||||
|
build_overlapping(50, per)
|
||||||
|
} else {
|
||||||
|
build_aliasing(50, per, &bridge)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|graph| {
|
||||||
|
let mut compiled = graph.compile().unwrap();
|
||||||
|
compiled.resolve(device).unwrap();
|
||||||
|
black_box(compiled.barriers().len())
|
||||||
|
},
|
||||||
|
BatchSize::LargeInput,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
group.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn benchmarks(criterion: &mut Criterion) {
|
||||||
|
let test = TestVulkan::acquire();
|
||||||
|
|
||||||
|
compile_benchmarks(criterion);
|
||||||
|
|
||||||
|
if let Some(test) = &test {
|
||||||
|
resolve_benchmarks(criterion, test.device());
|
||||||
|
} else {
|
||||||
|
eprintln!("Skipping compile_resolve benchmarks: no Vulkan device is available");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
criterion_group!(benches, benchmarks);
|
||||||
criterion_main!(benches);
|
criterion_main!(benches);
|
||||||
|
|
|
||||||
|
|
@ -2357,6 +2357,7 @@ impl<'a> GraphCompiler<'a> {
|
||||||
// for this barrier; prior barrier masks are not carried forward.
|
// for this barrier; prior barrier masks are not carried forward.
|
||||||
let read_only = crate::resources::access_flag_is_read(current.scope.access)
|
let read_only = crate::resources::access_flag_is_read(current.scope.access)
|
||||||
&& !access_flag_is_write(current.scope.access);
|
&& !access_flag_is_write(current.scope.access);
|
||||||
|
|
||||||
let can_skip_history_scan = read_only
|
let can_skip_history_scan = read_only
|
||||||
&& prior_access_summaries[current.resource.index()]
|
&& prior_access_summaries[current.resource.index()]
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
|
@ -2370,6 +2371,7 @@ impl<'a> GraphCompiler<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let previous = (!can_skip_history_scan)
|
let previous = (!can_skip_history_scan)
|
||||||
.then(|| &prior_accesses_by_resource[current.resource.index()])
|
.then(|| &prior_accesses_by_resource[current.resource.index()])
|
||||||
.and_then(|prior_accesses| {
|
.and_then(|prior_accesses| {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue