rendergraph: works!! (i cant believe it does!)

This commit is contained in:
Janis 2025-01-05 03:13:29 +01:00
parent 5b5a7cba54
commit a5ea706744
2 changed files with 166 additions and 66 deletions

View file

@ -26,6 +26,10 @@ pub fn egui_pre_pass(
// allocate resource ids for textures in tessellated list (imported from texture manager) // allocate resource ids for textures in tessellated list (imported from texture manager)
// define accesses for resource ids // define accesses for resource ids
if output.textures_delta.set.is_empty() {
return Ok(());
}
// create textures for new egui textures // create textures for new egui textures
for (egui_id, delta) in output for (egui_id, delta) in output
.textures_delta .textures_delta
@ -33,6 +37,7 @@ pub fn egui_pre_pass(
.iter() .iter()
.filter(|(_, image)| image.is_whole()) .filter(|(_, image)| image.is_whole())
{ {
tracing::trace!("creating texture image for egui image {egui_id:?}");
let image = Image::new( let image = Image::new(
dev.clone(), dev.clone(),
ImageDesc { ImageDesc {
@ -64,18 +69,23 @@ pub fn egui_pre_pass(
// calculate size for staging buffer. // calculate size for staging buffer.
// calculate size for staging image. // calculate size for staging image.
let (staging_size, image_size) = output.textures_delta.set.iter().fold( let (staging_size, image_size) = output.textures_delta.set.iter().fold(
(0usize, 0usize), (0usize, glam::UVec2::ZERO),
|(mut buffer, mut image), (_id, delta)| { |(mut buffer, mut image), (_id, delta)| {
let bytes = delta.image.height() * delta.image.width() * delta.image.bytes_per_pixel(); let bytes = delta.image.height() * delta.image.width() * delta.image.bytes_per_pixel();
if !delta.is_whole() { image = image.max(glam::uvec2(
image = image.max(bytes); delta.image.width() as u32,
} delta.image.height() as u32,
));
buffer = buffer + bytes; buffer = buffer + bytes;
(buffer, image) (buffer, image)
}, },
); );
tracing::trace!(
staging_size,
"creating staging buffer for uploading egui textures"
);
let mut staging_buffer = Buffer::new( let mut staging_buffer = Buffer::new(
dev.clone(), dev.clone(),
BufferDesc { BufferDesc {
@ -90,14 +100,16 @@ pub fn egui_pre_pass(
..Default::default() ..Default::default()
}, },
)?; )?;
tracing::trace!("creating staging image for uploading egui textures with dims={image_size:?}");
let staging_image = Arc::new(Image::new( let staging_image = Arc::new(Image::new(
dev.clone(), dev.clone(),
ImageDesc { ImageDesc {
name: Some("egui-prepass-staging-buffer".into()), name: Some("egui-prepass-staging-buffer".into()),
format: vk::Format::R8G8B8A8_UNORM, format: vk::Format::R8G8B8A8_UNORM,
extent: vk::Extent3D { extent: vk::Extent3D {
width: (image_size / 2) as u32, width: image_size.x,
height: (image_size - (image_size / 2)) as u32, height: image_size.y,
depth: 1, depth: 1,
}, },
usage: vk::ImageUsageFlags::TRANSFER_SRC | vk::ImageUsageFlags::TRANSFER_DST, usage: vk::ImageUsageFlags::TRANSFER_SRC | vk::ImageUsageFlags::TRANSFER_DST,
@ -108,6 +120,7 @@ pub fn egui_pre_pass(
)?); )?);
let aliased_images = { let aliased_images = {
tracing::trace!("mmap-ing staging buffer");
let mut staging_map = staging_buffer.map()?; let mut staging_map = staging_buffer.map()?;
let mut offset = 0; let mut offset = 0;
@ -115,7 +128,7 @@ pub fn egui_pre_pass(
.textures_delta .textures_delta
.set .set
.iter() .iter()
.filter_map(|(id, delta)| { .map(|(id, delta)| {
let bytes = let bytes =
delta.image.height() * delta.image.width() * delta.image.bytes_per_pixel(); delta.image.height() * delta.image.width() * delta.image.bytes_per_pixel();
@ -141,34 +154,12 @@ pub fn egui_pre_pass(
let old_offset = offset; let old_offset = offset;
offset += bytes; offset += bytes;
if !delta.is_whole() { let pos = delta.pos.unwrap_or_default();
unsafe {
let alias = staging_image
.clone()
.get_alias(ImageDesc {
name: Some(format!("egui-prepass-staging-aliased-{id:?}").into()),
format: vk::Format::R8G8B8A8_UNORM,
extent: vk::Extent3D {
width: delta.image.width() as u32,
height: delta.image.height() as u32,
depth: 1,
},
usage: vk::ImageUsageFlags::TRANSFER_SRC
| vk::ImageUsageFlags::TRANSFER_DST,
queue_families: device::QueueFlags::empty(),
..Default::default()
})
.unwrap();
let pos = delta.pos.unwrap();
let rect = Rect2D::new_from_size( let rect = Rect2D::new_from_size(
glam::ivec2(pos[0] as i32, pos[1] as i32), glam::ivec2(pos[0] as i32, pos[1] as i32),
glam::ivec2(delta.image.width() as i32, delta.image.height() as i32), glam::ivec2(delta.image.width() as i32, delta.image.height() as i32),
); );
Some((*id, (old_offset, bytes, rect, alias))) (*id, (old_offset, bytes, rect))
}
} else {
None
}
}) })
.collect::<BTreeMap<_, _>>(); .collect::<BTreeMap<_, _>>();
@ -210,7 +201,10 @@ pub fn egui_pre_pass(
let staging_image = ctx.get_image(staging_image).unwrap().clone(); let staging_image = ctx.get_image(staging_image).unwrap().clone();
let staging_buffer = ctx.get_buffer(staging_buffer).unwrap(); let staging_buffer = ctx.get_buffer(staging_buffer).unwrap();
for (id, (offset, _, rect, _)) in aliased_images { for (id, (offset, _, rect)) in aliased_images {
tracing::trace!(
"record-prepass: fetching alias of prepass staging image id={id:?}"
);
let alias = unsafe { let alias = unsafe {
staging_image.get_alias(ImageDesc { staging_image.get_alias(ImageDesc {
name: Some(format!("egui-prepass-staging-aliased-{id:?}v").into()), name: Some(format!("egui-prepass-staging-aliased-{id:?}v").into()),
@ -674,6 +668,36 @@ pub fn egui_pass(
); );
} }
let color_attachment = &vk::RenderingAttachmentInfo::default()
.image_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL)
.image_view(target.get_view(ImageViewDesc {
kind: vk::ImageViewType::TYPE_2D,
format: target.format(),
aspect: vk::ImageAspectFlags::COLOR,
..Default::default()
})?)
.load_op(vk::AttachmentLoadOp::LOAD)
.store_op(vk::AttachmentStoreOp::STORE);
cmd.begin_rendering(
vk::RenderingInfo::default()
.color_attachments(core::slice::from_ref(color_attachment))
.layer_count(1)
.render_area(vk::Rect2D::default().extent(target.extent_2d())),
);
cmd.set_scissors(&[vk::Rect2D::default()
.offset(vk::Offset2D::default())
.extent(target.extent_2d())]);
cmd.set_viewport(&[vk::Viewport::default()
.x(0.0)
.y(0.0)
.min_depth(0.0)
.max_depth(1.0)
.width(target.width() as f32)
.height(target.height() as f32)]);
cmd.bind_pipeline(&pipeline); cmd.bind_pipeline(&pipeline);
cmd.bind_indices(indices.buffer(), 0, vk::IndexType::UINT32); cmd.bind_indices(indices.buffer(), 0, vk::IndexType::UINT32);
cmd.bind_vertices(vertices.buffer(), 0); cmd.bind_vertices(vertices.buffer(), 0);
@ -696,6 +720,9 @@ pub fn egui_pass(
num_draw_calls as u32, num_draw_calls as u32,
size_of::<vk::DrawIndexedIndirectCommand>() as u32, size_of::<vk::DrawIndexedIndirectCommand>() as u32,
); );
cmd.end_rendering();
Ok(()) Ok(())
} }
}); });

View file

@ -12,7 +12,7 @@ use crate::{
device::{self, DeviceOwned}, device::{self, DeviceOwned},
images::{self, Image, ImageDesc}, images::{self, Image, ImageDesc},
sync, sync,
util::{self, Rgba}, util::{self, Rgba, WithLifetime},
SwapchainFrame, SwapchainFrame,
}; };
use ash::vk; use ash::vk;
@ -63,6 +63,7 @@ impl RenderContext<'_> {
self.resources.get(&id).and_then(|res| match res { self.resources.get(&id).and_then(|res| match res {
GraphResource::ImportedImage(arc) => Some(arc), GraphResource::ImportedImage(arc) => Some(arc),
GraphResource::Image(image) => Some(image), GraphResource::Image(image) => Some(image),
GraphResource::Framebuffer(fb) => Some(&fb.image),
_ => None, _ => None,
}) })
} }
@ -147,26 +148,33 @@ impl Access {
} }
pub fn color_attachment_read_only() -> Self { pub fn color_attachment_read_only() -> Self {
Self { Self {
stage: vk::PipelineStageFlags2::FRAGMENT_SHADER, stage: vk::PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT,
mask: vk::AccessFlags2::COLOR_ATTACHMENT_READ, mask: vk::AccessFlags2::COLOR_ATTACHMENT_READ,
layout: Some(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL), layout: Some(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL),
} }
} }
pub fn color_attachment_write_only() -> Self { pub fn color_attachment_write_only() -> Self {
Self { Self {
stage: vk::PipelineStageFlags2::FRAGMENT_SHADER, stage: vk::PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT,
mask: vk::AccessFlags2::COLOR_ATTACHMENT_WRITE, mask: vk::AccessFlags2::COLOR_ATTACHMENT_WRITE,
layout: Some(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL), layout: Some(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL),
} }
} }
pub fn color_attachment_read_write() -> Self { pub fn color_attachment_read_write() -> Self {
Self { Self {
stage: vk::PipelineStageFlags2::FRAGMENT_SHADER, stage: vk::PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT,
mask: vk::AccessFlags2::COLOR_ATTACHMENT_WRITE mask: vk::AccessFlags2::COLOR_ATTACHMENT_WRITE
| vk::AccessFlags2::COLOR_ATTACHMENT_READ, | vk::AccessFlags2::COLOR_ATTACHMENT_READ,
layout: Some(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL), layout: Some(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL),
} }
} }
pub fn present() -> Self {
Self {
stage: vk::PipelineStageFlags2::NONE,
mask: vk::AccessFlags2::empty(),
layout: Some(vk::ImageLayout::PRESENT_SRC_KHR),
}
}
} }
pub type RecordFn = dyn FnOnce(&RenderContext) -> crate::Result<()> + Send; pub type RecordFn = dyn FnOnce(&RenderContext) -> crate::Result<()> + Send;
@ -181,6 +189,16 @@ pub struct PassDesc {
pub record: Box<RecordFn>, pub record: Box<RecordFn>,
} }
impl Default for PassDesc {
fn default() -> Self {
Self {
reads: Default::default(),
writes: Default::default(),
record: Box::new(|_| Ok(())),
}
}
}
impl Debug for PassDesc { impl Debug for PassDesc {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PassDesc") f.debug_struct("PassDesc")
@ -254,6 +272,7 @@ impl RenderGraph {
pub fn import_framebuffer(&mut self, frame: Arc<SwapchainFrame>) -> GraphResourceId { pub fn import_framebuffer(&mut self, frame: Arc<SwapchainFrame>) -> GraphResourceId {
let id = GraphResourceId::new(); let id = GraphResourceId::new();
self.resources.insert(id, GraphResource::Framebuffer(frame)); self.resources.insert(id, GraphResource::Framebuffer(frame));
self.mark_as_output(id);
id id
} }
pub fn add_pass(&mut self, pass: PassDesc) { pub fn add_pass(&mut self, pass: PassDesc) {
@ -266,9 +285,10 @@ impl RenderGraph {
pub fn resolve( pub fn resolve(
&mut self, &mut self,
device: device::Device, device: device::Device,
) -> crate::Result<BTreeMap<GraphResourceId, GraphResource>> { ) -> crate::Result<WithLifetime<'_, commands::CommandList<commands::SingleUseCommand>>> {
// create internal resources: // create internal resources:
for (&id, desc) in self.resource_descs.iter() { for (&id, desc) in self.resource_descs.iter() {
tracing::trace!("creating resource {id:?} with {desc:?}");
match desc.clone() { match desc.clone() {
GraphResourceDesc::Image(image_desc) => { GraphResourceDesc::Image(image_desc) => {
self.resources.insert( self.resources.insert(
@ -285,7 +305,6 @@ impl RenderGraph {
} }
} }
eprintln!("{:#?}", &self);
let mut dag = petgraph::stable_graph::StableDiGraph::new(); let mut dag = petgraph::stable_graph::StableDiGraph::new();
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@ -319,6 +338,7 @@ impl RenderGraph {
for (rid, after) in read_accesses { for (rid, after) in read_accesses {
if let Some(&(other, before)) = last_write.get(&rid) { if let Some(&(other, before)) = last_write.get(&rid) {
tracing::trace!("adding edge between {other:?} and {node:?} for {rid:?} with ({before:?} -> {after:?})");
dag.add_edge(other, node, (rid, (before, after))); dag.add_edge(other, node, (rid, (before, after)));
} }
} }
@ -432,23 +452,29 @@ impl RenderGraph {
.collect::<Vec<_>>(); .collect::<Vec<_>>();
for ((from, to), edge) in edges { for ((from, to), edge) in edges {
tracing::trace!(
"adding additional edge between {from:?} and {to:?} for {:?} with ({:?} -> {:?})",
edge.0,
edge.1 .0,
edge.1 .1
);
dag.add_edge(from, to, edge); dag.add_edge(from, to, edge);
} }
#[cfg(any(debug_assertions, test))] // #[cfg(any(debug_assertions, test))]
std::fs::write( // std::fs::write(
"render_graph.dot", // "render_graph.dot",
&format!( // &format!(
"{:?}", // "{:?}",
petgraph::dot::Dot::with_attr_getters( // petgraph::dot::Dot::with_attr_getters(
&dag, // &dag,
&[], // &[],
&|_graph, edgeref| { format!("label = \"{:?}\"", edgeref.weight()) }, // &|_graph, edgeref| { format!("label = \"{:?}\"", edgeref.weight()) },
&|_graph, noderef| { format!("label = \"Pass({:?})\"", noderef.weight()) } // &|_graph, noderef| { format!("label = \"Pass({:?})\"", noderef.weight()) }
) // )
), // ),
) // )
.expect("writing render_graph repr"); // .expect("writing render_graph repr");
let mut topological_map = Vec::new(); let mut topological_map = Vec::new();
let mut top_dag = dag.clone(); let mut top_dag = dag.clone();
@ -457,7 +483,7 @@ impl RenderGraph {
loop { loop {
let (sinks, passes): (Vec<_>, Vec<_>) = top_dag let (sinks, passes): (Vec<_>, Vec<_>) = top_dag
.externals(petgraph::Direction::Outgoing) .externals(petgraph::Direction::Outgoing)
.filter(|&id| id != output) .filter(|&id| id != root)
.filter_map(|id| top_dag.node_weight(id).cloned().map(|idx| (id, idx))) .filter_map(|id| top_dag.node_weight(id).cloned().map(|idx| (id, idx)))
.unzip(); .unzip();
@ -488,13 +514,12 @@ impl RenderGraph {
} }
// I don't think this can currently happen with the way passes are added. // I don't think this can currently happen with the way passes are added.
top_dag.remove_node(output); top_dag.remove_node(root);
if top_dag.node_count() > 0 { if top_dag.node_count() > 0 {
eprintln!("dag: {top_dag:?}");
panic!("dag is cyclic!"); panic!("dag is cyclic!");
} }
eprintln!("topology: {:?}", topological_map);
let pool = let pool =
commands::SingleUseCommandPool::new(device.clone(), device.graphics_queue().clone())?; commands::SingleUseCommandPool::new(device.clone(), device.graphics_queue().clone())?;
@ -514,7 +539,7 @@ impl RenderGraph {
None None
} }
}) })
.map(|i| self.pass_descs.remove(i)) .map(|i| core::mem::take(&mut self.pass_descs[i]))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let cmd = pool.alloc()?; let cmd = pool.alloc()?;
@ -540,21 +565,32 @@ impl RenderGraph {
(pass.record)(&ctx)?; (pass.record)(&ctx)?;
} }
ctx.cmd.end()?;
crate::Result::Ok(ctx.cmd) crate::Result::Ok(ctx.cmd)
}) })
.collect::<crate::Result<Vec<_>>>()?; .collect::<crate::Result<Vec<_>>>()?;
let cmd_list = commands::CommandList(cmds); let cmd_list = commands::CommandList(cmds);
let future = cmd_list.submit(None, None, Arc::new(sync::Fence::create(device.clone())?))?; // let future = cmd_list.submit(None, None, Arc::new(sync::Fence::create(device.clone())?))?;
future.block()?; // future.block()?;
// let outputs = self
// .outputs
// .iter()
// .filter_map(|id| self.resources.remove(id).map(|res| (*id, res)))
// .collect::<BTreeMap<_, _>>();
Ok(WithLifetime::new(cmd_list))
}
pub fn get_outputs(&mut self) -> BTreeMap<GraphResourceId, GraphResource> {
let outputs = self let outputs = self
.outputs .outputs
.iter() .iter()
.filter_map(|id| self.resources.remove(id).map(|res| (*id, res))) .filter_map(|id| self.resources.remove(id).map(|res| (*id, res)))
.collect::<BTreeMap<_, _>>(); .collect::<BTreeMap<_, _>>();
Ok(outputs)
outputs
} }
pub fn transition_resource( pub fn transition_resource(
@ -566,7 +602,7 @@ impl RenderGraph {
) { ) {
let barrier: Barrier = match res { let barrier: Barrier = match res {
GraphResource::Framebuffer(arc) => { GraphResource::Framebuffer(arc) => {
image_barrier(arc.image, arc.format, from, to, None).into() image_barrier(arc.image.handle(), arc.image.format(), from, to, None).into()
} }
GraphResource::ImportedImage(arc) => { GraphResource::ImportedImage(arc) => {
image_barrier(arc.handle(), arc.format(), from, to, None).into() image_barrier(arc.handle(), arc.format(), from, to, None).into()
@ -737,7 +773,7 @@ pub fn image_barrier(
// vk::ImageLayout::DEPTH_ATTACHMENT_OPTIMAL}); // vk::ImageLayout::DEPTH_ATTACHMENT_OPTIMAL});
// def_dummy_pass!(RenderPass: { // def_dummy_pass!(RenderPass: {
// device::QueueFlags::GRAPHICS, // device::QueueFlags::GRAPHICS,
// vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL, // vk::ImageLayout::COLORiATTACHMENT_OPTIMAL,
// vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL}); // vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL});
// def_dummy_pass!(AsyncPass: { // def_dummy_pass!(AsyncPass: {
// device::QueueFlags::ASYNC_COMPUTE, // device::QueueFlags::ASYNC_COMPUTE,
@ -789,3 +825,40 @@ pub fn image_barrier(
// // graph.resolve(); // // graph.resolve();
// } // }
// } // }
pub fn clear_pass(rg: &mut RenderGraph, color: Rgba, target: GraphResourceId) {
let reads = [(target, Access::transfer_write())].to_vec();
let writes = [(target, Access::transfer_write())].to_vec();
let record: Box<RecordFn> = Box::new({
move |ctx| {
let target = ctx.get_image(target).unwrap();
let cmd = &ctx.cmd;
cmd.clear_color_image(
target.handle(),
target.format(),
vk::ImageLayout::TRANSFER_DST_OPTIMAL,
color,
&[images::SUBRESOURCERANGE_COLOR_ALL],
);
Ok(())
}
});
rg.add_pass(PassDesc {
reads,
writes,
record,
});
}
pub fn present_pass(rg: &mut RenderGraph, target: GraphResourceId) {
let record: Box<RecordFn> = Box::new(|_| Ok(()));
let reads = vec![(target, Access::present())];
let writes = vec![(target, Access::present())];
rg.add_pass(PassDesc {
reads,
writes,
record,
});
}