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)
// define accesses for resource ids
if output.textures_delta.set.is_empty() {
return Ok(());
}
// create textures for new egui textures
for (egui_id, delta) in output
.textures_delta
@ -33,6 +37,7 @@ pub fn egui_pre_pass(
.iter()
.filter(|(_, image)| image.is_whole())
{
tracing::trace!("creating texture image for egui image {egui_id:?}");
let image = Image::new(
dev.clone(),
ImageDesc {
@ -64,18 +69,23 @@ pub fn egui_pre_pass(
// calculate size for staging buffer.
// calculate size for staging image.
let (staging_size, image_size) = output.textures_delta.set.iter().fold(
(0usize, 0usize),
(0usize, glam::UVec2::ZERO),
|(mut buffer, mut image), (_id, delta)| {
let bytes = delta.image.height() * delta.image.width() * delta.image.bytes_per_pixel();
if !delta.is_whole() {
image = image.max(bytes);
}
image = image.max(glam::uvec2(
delta.image.width() as u32,
delta.image.height() as u32,
));
buffer = buffer + bytes;
(buffer, image)
},
);
tracing::trace!(
staging_size,
"creating staging buffer for uploading egui textures"
);
let mut staging_buffer = Buffer::new(
dev.clone(),
BufferDesc {
@ -90,14 +100,16 @@ pub fn egui_pre_pass(
..Default::default()
},
)?;
tracing::trace!("creating staging image for uploading egui textures with dims={image_size:?}");
let staging_image = Arc::new(Image::new(
dev.clone(),
ImageDesc {
name: Some("egui-prepass-staging-buffer".into()),
format: vk::Format::R8G8B8A8_UNORM,
extent: vk::Extent3D {
width: (image_size / 2) as u32,
height: (image_size - (image_size / 2)) as u32,
width: image_size.x,
height: image_size.y,
depth: 1,
},
usage: vk::ImageUsageFlags::TRANSFER_SRC | vk::ImageUsageFlags::TRANSFER_DST,
@ -108,6 +120,7 @@ pub fn egui_pre_pass(
)?);
let aliased_images = {
tracing::trace!("mmap-ing staging buffer");
let mut staging_map = staging_buffer.map()?;
let mut offset = 0;
@ -115,7 +128,7 @@ pub fn egui_pre_pass(
.textures_delta
.set
.iter()
.filter_map(|(id, delta)| {
.map(|(id, delta)| {
let bytes =
delta.image.height() * delta.image.width() * delta.image.bytes_per_pixel();
@ -141,34 +154,12 @@ pub fn egui_pre_pass(
let old_offset = offset;
offset += bytes;
if !delta.is_whole() {
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 pos = delta.pos.unwrap_or_default();
let rect = Rect2D::new_from_size(
glam::ivec2(pos[0] as i32, pos[1] as i32),
glam::ivec2(delta.image.width() as i32, delta.image.height() as i32),
);
Some((*id, (old_offset, bytes, rect, alias)))
}
} else {
None
}
(*id, (old_offset, bytes, rect))
})
.collect::<BTreeMap<_, _>>();
@ -210,7 +201,10 @@ pub fn egui_pre_pass(
let staging_image = ctx.get_image(staging_image).unwrap().clone();
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 {
staging_image.get_alias(ImageDesc {
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_indices(indices.buffer(), 0, vk::IndexType::UINT32);
cmd.bind_vertices(vertices.buffer(), 0);
@ -696,6 +720,9 @@ pub fn egui_pass(
num_draw_calls as u32,
size_of::<vk::DrawIndexedIndirectCommand>() as u32,
);
cmd.end_rendering();
Ok(())
}
});

View file

@ -12,7 +12,7 @@ use crate::{
device::{self, DeviceOwned},
images::{self, Image, ImageDesc},
sync,
util::{self, Rgba},
util::{self, Rgba, WithLifetime},
SwapchainFrame,
};
use ash::vk;
@ -63,6 +63,7 @@ impl RenderContext<'_> {
self.resources.get(&id).and_then(|res| match res {
GraphResource::ImportedImage(arc) => Some(arc),
GraphResource::Image(image) => Some(image),
GraphResource::Framebuffer(fb) => Some(&fb.image),
_ => None,
})
}
@ -147,26 +148,33 @@ impl Access {
}
pub fn color_attachment_read_only() -> Self {
Self {
stage: vk::PipelineStageFlags2::FRAGMENT_SHADER,
stage: vk::PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT,
mask: vk::AccessFlags2::COLOR_ATTACHMENT_READ,
layout: Some(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL),
}
}
pub fn color_attachment_write_only() -> Self {
Self {
stage: vk::PipelineStageFlags2::FRAGMENT_SHADER,
stage: vk::PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT,
mask: vk::AccessFlags2::COLOR_ATTACHMENT_WRITE,
layout: Some(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL),
}
}
pub fn color_attachment_read_write() -> Self {
Self {
stage: vk::PipelineStageFlags2::FRAGMENT_SHADER,
stage: vk::PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT,
mask: vk::AccessFlags2::COLOR_ATTACHMENT_WRITE
| vk::AccessFlags2::COLOR_ATTACHMENT_READ,
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;
@ -181,6 +189,16 @@ pub struct PassDesc {
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 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PassDesc")
@ -254,6 +272,7 @@ impl RenderGraph {
pub fn import_framebuffer(&mut self, frame: Arc<SwapchainFrame>) -> GraphResourceId {
let id = GraphResourceId::new();
self.resources.insert(id, GraphResource::Framebuffer(frame));
self.mark_as_output(id);
id
}
pub fn add_pass(&mut self, pass: PassDesc) {
@ -266,9 +285,10 @@ impl RenderGraph {
pub fn resolve(
&mut self,
device: device::Device,
) -> crate::Result<BTreeMap<GraphResourceId, GraphResource>> {
) -> crate::Result<WithLifetime<'_, commands::CommandList<commands::SingleUseCommand>>> {
// create internal resources:
for (&id, desc) in self.resource_descs.iter() {
tracing::trace!("creating resource {id:?} with {desc:?}");
match desc.clone() {
GraphResourceDesc::Image(image_desc) => {
self.resources.insert(
@ -285,7 +305,6 @@ impl RenderGraph {
}
}
eprintln!("{:#?}", &self);
let mut dag = petgraph::stable_graph::StableDiGraph::new();
#[derive(Debug, Clone, Copy)]
@ -319,6 +338,7 @@ impl RenderGraph {
for (rid, after) in read_accesses {
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)));
}
}
@ -432,23 +452,29 @@ impl RenderGraph {
.collect::<Vec<_>>();
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);
}
#[cfg(any(debug_assertions, test))]
std::fs::write(
"render_graph.dot",
&format!(
"{:?}",
petgraph::dot::Dot::with_attr_getters(
&dag,
&[],
&|_graph, edgeref| { format!("label = \"{:?}\"", edgeref.weight()) },
&|_graph, noderef| { format!("label = \"Pass({:?})\"", noderef.weight()) }
)
),
)
.expect("writing render_graph repr");
// #[cfg(any(debug_assertions, test))]
// std::fs::write(
// "render_graph.dot",
// &format!(
// "{:?}",
// petgraph::dot::Dot::with_attr_getters(
// &dag,
// &[],
// &|_graph, edgeref| { format!("label = \"{:?}\"", edgeref.weight()) },
// &|_graph, noderef| { format!("label = \"Pass({:?})\"", noderef.weight()) }
// )
// ),
// )
// .expect("writing render_graph repr");
let mut topological_map = Vec::new();
let mut top_dag = dag.clone();
@ -457,7 +483,7 @@ impl RenderGraph {
loop {
let (sinks, passes): (Vec<_>, Vec<_>) = top_dag
.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)))
.unzip();
@ -488,13 +514,12 @@ impl RenderGraph {
}
// 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 {
eprintln!("dag: {top_dag:?}");
panic!("dag is cyclic!");
}
eprintln!("topology: {:?}", topological_map);
let pool =
commands::SingleUseCommandPool::new(device.clone(), device.graphics_queue().clone())?;
@ -514,7 +539,7 @@ impl RenderGraph {
None
}
})
.map(|i| self.pass_descs.remove(i))
.map(|i| core::mem::take(&mut self.pass_descs[i]))
.collect::<Vec<_>>();
let cmd = pool.alloc()?;
@ -540,21 +565,32 @@ impl RenderGraph {
(pass.record)(&ctx)?;
}
ctx.cmd.end()?;
crate::Result::Ok(ctx.cmd)
})
.collect::<crate::Result<Vec<_>>>()?;
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
.outputs
.iter()
.filter_map(|id| self.resources.remove(id).map(|res| (*id, res)))
.collect::<BTreeMap<_, _>>();
Ok(outputs)
outputs
}
pub fn transition_resource(
@ -566,7 +602,7 @@ impl RenderGraph {
) {
let barrier: Barrier = match res {
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) => {
image_barrier(arc.handle(), arc.format(), from, to, None).into()
@ -737,7 +773,7 @@ pub fn image_barrier(
// vk::ImageLayout::DEPTH_ATTACHMENT_OPTIMAL});
// def_dummy_pass!(RenderPass: {
// device::QueueFlags::GRAPHICS,
// vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL,
// vk::ImageLayout::COLORiATTACHMENT_OPTIMAL,
// vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL});
// def_dummy_pass!(AsyncPass: {
// device::QueueFlags::ASYNC_COMPUTE,
@ -789,3 +825,40 @@ pub fn image_barrier(
// // 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,
});
}