Optimize render graph and add GPU profiling

Amp-Thread-ID: https://ampcode.com/threads/T-01a01380-b478-77d0-84a0-102880a5c5ae
Co-authored-by: Heaust Azure <heaust.azure@gmail.com>
This commit is contained in:
Amp
2026-08-20 11:48:06 +00:00
co-authored by heaust
parent ded70a3cb4
commit 000bfd63bf
22 changed files with 1622 additions and 203 deletions
+100 -3
View File
@@ -12,6 +12,14 @@ pub struct RenderGraph {
#[serde(default)]
pub pipelines: PipelineDeclarations,
pub passes: Vec<Pass>,
#[serde(skip)]
pub executions: Vec<Execution>,
}
#[derive(Clone)]
pub enum Execution {
Compute(usize),
Render(Vec<usize>),
}
#[derive(Clone, Default, Deserialize)]
@@ -30,6 +38,8 @@ pub struct Buffer {
pub array: String,
#[serde(default)]
pub usage: Vec<String>,
#[serde(default = "frame_sync")]
pub sync: String,
}
#[derive(Clone, Deserialize, Serialize)]
@@ -186,7 +196,7 @@ pub struct Pass {
pub dispatch: [u32; 3],
}
#[derive(Clone, Deserialize)]
#[derive(Clone, Deserialize, PartialEq, Eq)]
pub struct Binding {
#[serde(default)]
pub group: u32,
@@ -318,7 +328,9 @@ impl RenderGraph {
for pass in &mut self.passes {
pass.dependencies = pass.after.iter().map(|id| sorted_ids[id]).collect();
}
self.plan_resources()
self.plan_resources()?;
self.plan_execution();
Ok(())
}
fn plan_resources(&mut self) -> Result<(), &'static str> {
@@ -391,6 +403,80 @@ impl RenderGraph {
self.validate_ids()
}
fn plan_execution(&mut self) {
let mut executions = Vec::new();
for index in 0..self.passes.len() {
let merge = executions.last().is_some_and(|execution| match execution {
Execution::Render(passes) => self.can_merge_render(*passes.last().unwrap(), index),
Execution::Compute(_) => false,
});
if merge {
let Some(Execution::Render(passes)) = executions.last_mut() else {
unreachable!()
};
passes.push(index);
} else if self.passes[index].kind == "render" {
executions.push(Execution::Render(vec![index]));
} else {
executions.push(Execution::Compute(index));
}
}
self.executions = executions;
}
fn can_merge_render(&self, previous: usize, next: usize) -> bool {
let previous = &self.passes[previous];
let next = &self.passes[next];
if next.kind != "render"
|| previous.color.len() != next.color.len()
|| self.sample_count(&previous.pipeline) != self.sample_count(&next.pipeline)
|| previous
.color
.iter()
.zip(&next.color)
.any(|(previous, next)| {
previous.resource != next.resource
|| previous.store != "store"
|| next.load != "load"
})
{
return false;
}
let same_depth = match (&previous.depth, &next.depth) {
(None, None) => true,
(Some(previous), Some(next)) => {
previous.resource == next.resource
&& previous.store == "store"
&& next.load == "load"
}
_ => false,
};
same_depth
&& !next.bindings.iter().any(|binding| {
next.color
.iter()
.any(|attachment| attachment.resource == binding.resource)
|| next
.depth
.as_ref()
.is_some_and(|attachment| attachment.resource == binding.resource)
})
}
fn sample_count(&self, pipeline: &str) -> Option<u64> {
self.pipelines
.render
.iter()
.find(|value| value.id == pipeline)
.map(|value| {
value
.multisample
.get("count")
.and_then(Value::as_u64)
.unwrap_or(1)
})
}
fn validate_ids(&self) -> Result<(), &'static str> {
let mut resources = HashSet::new();
for id in self
@@ -405,6 +491,14 @@ impl RenderGraph {
return Err("GRAPH_RESOURCE");
}
}
if self
.resources
.buffers
.iter()
.any(|buffer| !matches!(buffer.sync.as_str(), "frame" | "loadout"))
{
return Err("GRAPH_BUFFER_SYNC");
}
let mut pipelines = HashSet::new();
for id in self
.pipelines
@@ -443,7 +537,7 @@ impl Pass {
}
impl Texture {
fn key(&self) -> Result<String, &'static str> {
pub(crate) fn key(&self) -> Result<String, &'static str> {
let mut usage = self.usage.clone();
usage.sort_unstable();
usage.dedup();
@@ -498,3 +592,6 @@ fn index_format() -> String {
fn dispatch() -> [u32; 3] {
[1, 1, 1]
}
fn frame_sync() -> String {
"frame".into()
}