diff --git a/crates/renderer/src/egui.rs b/crates/renderer/src/egui.rs index d9cfc1d..3be5620 100644 --- a/crates/renderer/src/egui.rs +++ b/crates/renderer/src/egui.rs @@ -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 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 - } + 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), + ); + (*id, (old_offset, bytes, rect)) }) .collect::>(); @@ -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::() as u32, ); + + cmd.end_rendering(); + Ok(()) } }); diff --git a/crates/renderer/src/render_graph.rs b/crates/renderer/src/render_graph.rs index fbad2a5..583da85 100644 --- a/crates/renderer/src/render_graph.rs +++ b/crates/renderer/src/render_graph.rs @@ -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, } +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) -> 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> { + ) -> crate::Result>> { // 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::>(); 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::>(); let cmd = pool.alloc()?; @@ -540,21 +565,32 @@ impl RenderGraph { (pass.record)(&ctx)?; } + ctx.cmd.end()?; crate::Result::Ok(ctx.cmd) }) .collect::>>()?; 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::>(); + Ok(WithLifetime::new(cmd_list)) + } + + pub fn get_outputs(&mut self) -> BTreeMap { let outputs = self .outputs .iter() .filter_map(|id| self.resources.remove(id).map(|res| (*id, res))) .collect::>(); - 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 = 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 = Box::new(|_| Ok(())); + let reads = vec![(target, Access::present())]; + let writes = vec![(target, Access::present())]; + rg.add_pass(PassDesc { + reads, + writes, + record, + }); +}