rendergraph: works!! (i cant believe it does!)
This commit is contained in:
parent
5b5a7cba54
commit
a5ea706744
|
@ -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::<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(())
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue