use concinnity_testing::source::relative_rust_sources;
use std::collections::BTreeMap;
use std::path::Path;
const VULKAN_ROOT: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/src/vulkan");
const DIRECTX_ROOT: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/src/directx");
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
enum Reason {
GraphDriven,
IntraPass,
Upload,
OutOfFrame,
Present,
Ungraphed,
Inline,
RecordingSurface,
AttachmentLayout,
}
struct BackendAudit {
backend: &'static str,
root: &'static str,
calls: &'static [&'static str],
sites: &'static [(&'static str, &'static str, usize, Reason)],
}
const AUDITS: &[BackendAudit] = &[
BackendAudit {
backend: "vulkan",
root: VULKAN_ROOT,
calls: &[
"cmd_pipeline_barrier",
"rec.pipeline_barrier(",
".final_layout(",
],
sites: &[
(
"graph_exec.rs",
"cmd_pipeline_barrier",
4,
Reason::GraphDriven,
),
("hiz.rs", "rec.pipeline_barrier(", 1, Reason::IntraPass),
(
"record.rs",
"cmd_pipeline_barrier",
1,
Reason::RecordingSurface,
),
(
"reactive_mask.rs",
"cmd_pipeline_barrier",
2,
Reason::IntraPass,
),
("texture.rs", "cmd_pipeline_barrier", 1, Reason::Upload),
(
"geometry_upload.rs",
"cmd_pipeline_barrier",
2,
Reason::Upload,
),
("probe_set.rs", "cmd_pipeline_barrier", 1, Reason::Upload),
("probe.rs", "cmd_pipeline_barrier", 3, Reason::OutOfFrame),
(
"probe_prefilter.rs",
"cmd_pipeline_barrier",
2,
Reason::OutOfFrame,
),
("raytrace.rs", "cmd_pipeline_barrier", 3, Reason::OutOfFrame),
(
"screenshot.rs",
"cmd_pipeline_barrier",
2,
Reason::OutOfFrame,
),
(
"auto_exposure.rs",
"rec.pipeline_barrier(",
4,
Reason::Ungraphed,
),
("cull.rs", "cmd_pipeline_barrier", 1, Reason::Ungraphed),
(
"post/gbuffer.rs",
"cmd_pipeline_barrier",
1,
Reason::Ungraphed,
),
("planar.rs", "cmd_pipeline_barrier", 2, Reason::Ungraphed),
("planar.rs", "rec.pipeline_barrier(", 2, Reason::Ungraphed),
(
"transparent.rs",
"cmd_pipeline_barrier",
2,
Reason::IntraPass,
),
("raymarch.rs", "cmd_pipeline_barrier", 2, Reason::IntraPass),
("draw/main.rs", "cmd_pipeline_barrier", 1, Reason::Inline),
(
"post/upscale/mod.rs",
"cmd_pipeline_barrier",
1,
Reason::Inline,
),
(
"render_pass.rs",
".final_layout(",
8,
Reason::AttachmentLayout,
),
("decal.rs", ".final_layout(", 1, Reason::AttachmentLayout),
("fog.rs", ".final_layout(", 1, Reason::AttachmentLayout),
(
"transparent.rs",
".final_layout(",
3,
Reason::AttachmentLayout,
),
("line.rs", ".final_layout(", 1, Reason::AttachmentLayout),
("particle.rs", ".final_layout(", 1, Reason::AttachmentLayout),
(
"reactive_mask.rs",
".final_layout(",
1,
Reason::AttachmentLayout,
),
("raymarch.rs", ".final_layout(", 2, Reason::AttachmentLayout),
(
"post/gbuffer.rs",
".final_layout(",
4,
Reason::AttachmentLayout,
),
(
"post/rt_reflections.rs",
".final_layout(",
1,
Reason::AttachmentLayout,
),
(
"post/pass_cache.rs",
".final_layout(",
1,
Reason::AttachmentLayout,
),
],
},
BackendAudit {
backend: "directx",
root: DIRECTX_ROOT,
calls: &[".ResourceBarrier("],
sites: &[
("graph_exec.rs", ".ResourceBarrier(", 1, Reason::GraphDriven),
("hiz.rs", ".ResourceBarrier(", 1, Reason::IntraPass),
("particle.rs", ".ResourceBarrier(", 2, Reason::IntraPass),
("draw/main.rs", ".ResourceBarrier(", 2, Reason::IntraPass),
("raymarch.rs", ".ResourceBarrier(", 4, Reason::IntraPass),
("transparent.rs", ".ResourceBarrier(", 4, Reason::IntraPass),
(
"reactive_mask.rs",
".ResourceBarrier(",
3,
Reason::IntraPass,
),
("post/ssgi.rs", ".ResourceBarrier(", 1, Reason::IntraPass),
(
"post/post_device.rs",
".ResourceBarrier(",
2,
Reason::IntraPass,
),
("post/gbuffer.rs", ".ResourceBarrier(", 1, Reason::IntraPass),
(
"post/rt_reflections.rs",
".ResourceBarrier(",
2,
Reason::IntraPass,
),
("post/bloom.rs", ".ResourceBarrier(", 2, Reason::IntraPass),
("allocator.rs", ".ResourceBarrier(", 1, Reason::Upload),
("geometry_upload.rs", ".ResourceBarrier(", 4, Reason::Upload),
(
"resources/geometry.rs",
".ResourceBarrier(",
2,
Reason::Upload,
),
(
"resources/skinning.rs",
".ResourceBarrier(",
1,
Reason::Upload,
),
(
"resources/streaming.rs",
".ResourceBarrier(",
2,
Reason::Upload,
),
("texture.rs", ".ResourceBarrier(", 6, Reason::Upload),
("transient_pool.rs", ".ResourceBarrier(", 2, Reason::Upload),
(
"resources/geometry_rebuild.rs",
".ResourceBarrier(",
6,
Reason::OutOfFrame,
),
("probe.rs", ".ResourceBarrier(", 6, Reason::OutOfFrame),
(
"probe_prefilter.rs",
".ResourceBarrier(",
3,
Reason::OutOfFrame,
),
("raytrace.rs", ".ResourceBarrier(", 11, Reason::OutOfFrame),
("screenshot.rs", ".ResourceBarrier(", 2, Reason::OutOfFrame),
("draw/composite.rs", ".ResourceBarrier(", 2, Reason::Present),
(
"auto_exposure.rs",
".ResourceBarrier(",
4,
Reason::Ungraphed,
),
("cull.rs", ".ResourceBarrier(", 4, Reason::Ungraphed),
("planar.rs", ".ResourceBarrier(", 6, Reason::Ungraphed),
(
"post/upscale/mod.rs",
".ResourceBarrier(",
2,
Reason::Inline,
),
],
},
];
fn call_sites(source: &str, call: &str) -> usize {
source
.lines()
.map(|line| line.split("//").next().unwrap_or(""))
.map(|code| code.matches(call).count())
.sum()
}
fn scan(audit: &BackendAudit) -> BTreeMap<(String, &'static str), usize> {
let files = relative_rust_sources(Path::new(audit.root));
let mut found = BTreeMap::new();
for (rel, path) in files {
let source = std::fs::read_to_string(&path).unwrap_or_else(|e| panic!("read {rel}: {e}"));
for &call in audit.calls {
let n = call_sites(&source, call);
if n > 0 {
found.insert((rel.clone(), call), n);
}
}
}
found
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_barrier_call_site_is_classified() {
let mut failures = Vec::new();
for audit in AUDITS {
let found = scan(audit);
let expected: BTreeMap<(String, &str), (usize, Reason)> = audit
.sites
.iter()
.map(|&(path, call, n, reason)| ((path.to_string(), call), (n, reason)))
.collect();
for ((path, call), &n) in &found {
match expected.get(&(path.clone(), *call)) {
None => failures.push(format!(
"{}/{path}: {n} `{call}` site(s) with no entry in the audit table; \
classify them (or move them into the graph executor)",
audit.backend
)),
Some(&(want, _)) if want != n => failures.push(format!(
"{}/{path}: audit table says {want} `{call}` site(s), found {n}; \
update the table",
audit.backend
)),
Some(_) => {}
}
}
for ((path, call), (want, reason)) in &expected {
if !found.contains_key(&(path.clone(), *call)) {
failures.push(format!(
"{}/{path}: audit table says {want} `{call}` site(s) ({reason:?}), \
found none; drop the entry",
audit.backend
));
}
}
}
assert!(failures.is_empty(), "\n{}", failures.join("\n"));
}
#[test]
fn the_scan_ignores_barriers_named_in_comments() {
assert_eq!(
call_sites("// feeds cmd_pipeline_barrier\n", "cmd_pipeline_barrier"),
0
);
assert_eq!(
call_sites(" device.cmd_pipeline_barrier(\n", "cmd_pipeline_barrier"),
1
);
assert_eq!(
call_sites(
" cmd.ResourceBarrier(&[b]); // transition\n",
".ResourceBarrier("
),
1
);
}
#[test]
fn the_scan_reaches_nested_backend_modules() {
let vulkan = scan(&AUDITS[0]);
assert!(
vulkan.keys().any(|(p, _)| p.contains('/')),
"expected nested paths in the vulkan scan, got {:?}",
vulkan.keys().collect::<Vec<_>>()
);
}
}