- Add BeginRendering/EndRendering commands with imported/created attachment
views, view plumbing, and aspect-gated depth/stencil slots
- Derive and validate render-pass spans at compile time; contract spans into
single DAG nodes before hazard analysis; hoist cross-boundary barriers to
pass begins with in-pass policy and created-view resolution
- Replace the four-pass ordering pipeline with a single command walk
(build_command_orderings): span derivation, validation, lifetime-only
injection, dependency edges, and Kahn batching in one pass; batches are
ranges into execution_order and RenderPassSpan keeps only begin/end
- Perf: node-collapsed frontier analysis, dense command/resource indexing
(no SipHash on hot paths), inline barrier storage, exact preallocation,
lazy Kahn ready heap, scoped barrier fast paths
- Scope buffer and image alias barriers to the incoming resource per the
Vulkan resource-memory-aliasing rules (availability unscoped, visibility
scoped to the new owner)
- Add criterion compile benchmarks (two_pass/{no_pass,pass}/per_{20,50,100})
142 lines
3.7 KiB
Rust
142 lines
3.7 KiB
Rust
//! These graph shapes compare command-level batching with render-pass span
|
|
//! contraction and barrier hoisting. Each has two disjoint buffer access sets
|
|
//! joined by a shared-buffer dependency across the passes.
|
|
|
|
use std::{hint::black_box, time::Duration};
|
|
|
|
use ash::vk;
|
|
use criterion::{BatchSize, Criterion, criterion_group, criterion_main};
|
|
use renderer_rendergraph::{
|
|
Buffer, BufferRange, GraphBufferDesc, RenderGraph,
|
|
commands::{BeginRendering, Command, EndRendering},
|
|
recorder::{CommandIndex, CommandRecorder, RecordError, RecordedCommand, SideEffectMap},
|
|
resources::{SubresourceScope, SubresourceScopeInner},
|
|
};
|
|
|
|
struct Use {
|
|
buffer: Buffer,
|
|
write: bool,
|
|
}
|
|
|
|
impl Command for Use {
|
|
fn side_effects(&self, command_index: CommandIndex, map: &mut SideEffectMap) {
|
|
map.insert(
|
|
command_index,
|
|
self.buffer.id(),
|
|
SubresourceScope {
|
|
stage: vk::PipelineStageFlags2::COMPUTE_SHADER,
|
|
access: if self.write {
|
|
vk::AccessFlags2::SHADER_WRITE
|
|
} else {
|
|
vk::AccessFlags2::SHADER_READ
|
|
},
|
|
subresource: SubresourceScopeInner::Buffer(BufferRange::full()),
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
impl RecordedCommand for Use {
|
|
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 {
|
|
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 shared = graph.create_buffer(GraphBufferDesc {
|
|
size: 4096,
|
|
..Default::default()
|
|
});
|
|
|
|
if pass {
|
|
graph.push(BeginRendering {
|
|
color: vec![],
|
|
depth: None,
|
|
});
|
|
}
|
|
for _ in 0..per {
|
|
for &buffer in &a {
|
|
graph.push(Use {
|
|
buffer,
|
|
write: true,
|
|
});
|
|
}
|
|
}
|
|
graph.push(Use {
|
|
buffer: shared,
|
|
write: true,
|
|
});
|
|
if pass {
|
|
graph.push(EndRendering);
|
|
graph.push(BeginRendering {
|
|
color: vec![],
|
|
depth: None,
|
|
});
|
|
}
|
|
for _ in 0..per {
|
|
for &buffer in &b {
|
|
graph.push(Use {
|
|
buffer,
|
|
write: true,
|
|
});
|
|
}
|
|
}
|
|
graph.push(Use {
|
|
buffer: shared,
|
|
write: false,
|
|
});
|
|
if pass {
|
|
graph.push(EndRendering);
|
|
}
|
|
|
|
graph
|
|
}
|
|
|
|
fn compile_benchmarks(criterion: &mut Criterion) {
|
|
let mut group = criterion.benchmark_group("compile");
|
|
group
|
|
.measurement_time(Duration::from_secs(4))
|
|
.sample_size(20);
|
|
|
|
for (pass_name, pass) in [("no_pass", false), ("pass", true)] {
|
|
for per in [20, 50, 100] {
|
|
group.bench_function(format!("two_pass/{pass_name}/per_{per}"), |bencher| {
|
|
bencher.iter_batched(
|
|
|| build(50, per, pass),
|
|
|graph| {
|
|
let compiled = graph.compile().unwrap();
|
|
black_box(compiled.barriers().len())
|
|
},
|
|
BatchSize::LargeInput,
|
|
);
|
|
});
|
|
}
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
criterion_group!(benches, compile_benchmarks);
|
|
criterion_main!(benches);
|