stuff: slight refactoring

This commit is contained in:
Janis 2025-01-18 14:49:25 +01:00
parent 9802bec8b0
commit c70c1591db
7 changed files with 101 additions and 597 deletions

View file

@ -6,6 +6,8 @@ edition = "2021"
[dependencies] [dependencies]
winit = { workspace = true } winit = { workspace = true }
tracing = { workspace = true } tracing = { workspace = true }
smol = { workspace = true }
glam = { workspace = true }
tracing-subscriber = { workspace = true } tracing-subscriber = { workspace = true }
renderer = { path = "../renderer" } renderer = { path = "../renderer" }

View file

@ -444,12 +444,12 @@ impl SingleUseCommand {
); );
} }
} }
pub fn bind_vertices(&self, buffer: vk::Buffer, offset: u64) { pub fn bind_vertex_buffers(&self, buffers: &[vk::Buffer], offsets: &[u64]) {
assert_eq!(self.state(), CommandBufferState::Recording); assert_eq!(self.state(), CommandBufferState::Recording);
unsafe { unsafe {
self.device() self.device()
.dev() .dev()
.cmd_bind_vertex_buffers(self.buffer(), 0, &[buffer], &[offset]); .cmd_bind_vertex_buffers(self.buffer(), 0, buffers, offsets);
} }
} }
pub fn bind_indices(&self, buffer: vk::Buffer, offset: u64, kind: vk::IndexType) { pub fn bind_indices(&self, buffer: vk::Buffer, offset: u64, kind: vk::IndexType) {

View file

@ -245,6 +245,9 @@ impl Device {
pub fn graphics_queue(&self) -> &Queue { pub fn graphics_queue(&self) -> &Queue {
&self.0.main_queue &self.0.main_queue
} }
pub fn transfer_queue(&self) -> &Queue {
&self.0.transfer_queue
}
pub fn present_queue(&self) -> &Queue { pub fn present_queue(&self) -> &Queue {
&self.0.present_queue &self.0.present_queue
} }

View file

@ -712,7 +712,7 @@ pub fn egui_pass(
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_vertex_buffers(&[vertices.buffer()], &[0]);
cmd.push_constants( cmd.push_constants(
&pipeline_layout, &pipeline_layout,
vk::ShaderStageFlags::VERTEX, vk::ShaderStageFlags::VERTEX,

View file

@ -43,9 +43,9 @@ mod egui_pass;
mod images; mod images;
mod pipeline; mod pipeline;
mod render_graph; mod render_graph;
mod rendering; pub mod rendering;
mod sync; mod sync;
mod util; pub mod util;
use device::{Device, DeviceOwned, DeviceQueueFamilies}; use device::{Device, DeviceOwned, DeviceQueueFamilies};
@ -110,7 +110,7 @@ pub enum Error {
Io(#[from] std::io::Error), Io(#[from] std::io::Error),
} }
type Result<T> = core::result::Result<T, Error>; pub type Result<T> = core::result::Result<T, Error>;
struct VkNameList<'a> { struct VkNameList<'a> {
names: Vec<*const i8>, names: Vec<*const i8>,
@ -1072,7 +1072,7 @@ impl Drop for Surface {
} }
} }
struct SamplerCache { pub struct SamplerCache {
device: Device, device: Device,
samplers: HashMap<pipeline::SamplerDesc, pipeline::Sampler>, samplers: HashMap<pipeline::SamplerDesc, pipeline::Sampler>,
} }
@ -1101,7 +1101,7 @@ impl SamplerCache {
pub struct Vulkan { pub struct Vulkan {
instance: Arc<Instance>, instance: Arc<Instance>,
device: Device, pub device: Device,
samplers: SamplerCache, samplers: SamplerCache,
} }
@ -1124,6 +1124,14 @@ impl Vulkan {
const NSIGHT_INTERCEPTION_LAYER_NAME: &'static core::ffi::CStr = const NSIGHT_INTERCEPTION_LAYER_NAME: &'static core::ffi::CStr =
c"VK_LAYER_NV_nomad_release_public_2021_4_2"; c"VK_LAYER_NV_nomad_release_public_2021_4_2";
pub fn samplers(&self) -> &SamplerCache {
&self.samplers
}
pub fn samplers_mut(&mut self) -> &mut SamplerCache {
&mut self.samplers
}
pub fn new( pub fn new(
app_name: &str, app_name: &str,
instance_layers: &[&CStr], instance_layers: &[&CStr],
@ -1232,6 +1240,7 @@ impl Vulkan {
.features10( .features10(
vk::PhysicalDeviceFeatures::default() vk::PhysicalDeviceFeatures::default()
.sampler_anisotropy(true) .sampler_anisotropy(true)
.fill_mode_non_solid(true)
.multi_draw_indirect(true), .multi_draw_indirect(true),
) )
.features11(vk::PhysicalDeviceVulkan11Features::default().shader_draw_parameters(true)) .features11(vk::PhysicalDeviceVulkan11Features::default().shader_draw_parameters(true))
@ -2049,7 +2058,7 @@ pub struct Renderer<W> {
// thinkw: want renderer linked with display? then no (real) headless // thinkw: want renderer linked with display? then no (real) headless
display: RawDisplayHandle, display: RawDisplayHandle,
pub window_contexts: HashMap<W, WindowContext>, pub window_contexts: HashMap<W, WindowContext>,
vulkan: Vulkan, pub vulkan: Vulkan,
} }
pub use vk::Extent2D; pub use vk::Extent2D;
@ -2066,447 +2075,59 @@ impl<W> Renderer<W> {
}) })
} }
pub fn draw_egui(&mut self, ctx: &egui::Context, output: egui::FullOutput) -> Result<()> { pub async fn draw_with_graph<K, F, G, T>(
let pool = commands::SingleUseCommandPool::new( &mut self,
self.vulkan.device.clone(), window: &K,
self.vulkan.device.graphics_queue().clone(), pre_present_cb: F,
) cb: G,
.unwrap(); ) -> Result<T>
let cmd = pool.alloc().unwrap(); where
K: core::hash::Hash + Eq,
let cmd_objects = output W: core::hash::Hash + Eq + Borrow<K>,
.textures_delta F: FnOnce(),
.set G: FnOnce(&mut Self, &mut render_graph::RenderGraph) -> T,
.iter()
.map(|(egui_id, delta)| {
let size = delta.image.size();
let byte_size = size[0] * size[1] * 4;
let mut staging = buffers::Buffer::new(
self.vulkan.device.clone(),
buffers::BufferDesc {
name: Some(format!("egui-{egui_id:?}-staging-buf").into()),
size: byte_size as u64,
usage: vk::BufferUsageFlags::TRANSFER_SRC,
mem_usage: vk_mem::MemoryUsage::AutoPreferHost,
alloc_flags: vk_mem::AllocationCreateFlags::MAPPED
| vk_mem::AllocationCreateFlags::HOST_ACCESS_SEQUENTIAL_WRITE
| vk_mem::AllocationCreateFlags::STRATEGY_FIRST_FIT,
..Default::default()
},
)
.expect("staging buffer");
{ {
let mut mem = staging.map().expect("mapping staging buffer"); let dev = self.vulkan.device.clone();
match &delta.image {
egui::ImageData::Color(arc) => { let ctx = self.window_contexts.get(window).unwrap();
let slice = unsafe { let (frame, suboptimal) = ctx.current_swapchain.read().clone().acquire_image().await?;
core::slice::from_raw_parts(
arc.pixels.as_ptr().cast::<u8>(), if suboptimal {
arc.pixels.len() * size_of::<Color32>(), tracing::warn!(
) "swapchain ({:?}) is suboptimal!",
}; ctx.current_swapchain.read().swapchain
mem[..slice.len()].copy_from_slice(slice); );
}
egui::ImageData::Font(font_image) => {
for (i, c) in font_image.srgba_pixels(None).enumerate() {
let bytes = c.to_array();
mem[i * 4..(i + 1) * 4].copy_from_slice(&bytes);
}
}
}
} }
let sampled = if delta.is_whole() { let [r, g, b] =
vk::ImageUsageFlags::SAMPLED rand::prelude::StdRng::seed_from_u64(ctx.surface.surface.as_raw()).gen::<[f32; 3]>();
} else { let clear_color = Rgba([r, g, b, 1.0]);
vk::ImageUsageFlags::TRANSFER_SRC
};
let texture = Arc::new( let mut rg = render_graph::RenderGraph::new();
images::Image::new( let framebuffer = rg.import_image(frame.image.clone(), Access::undefined());
self.vulkan.device.clone(), rg.mark_as_output(framebuffer, Access::present());
images::ImageDesc { rg.framebuffer = Some(framebuffer);
name: Some(format!("egui-texture-{egui_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: sampled | vk::ImageUsageFlags::TRANSFER_DST,
mem_usage: vk_mem::MemoryUsage::AutoPreferDevice,
..Default::default()
},
)
.expect("image creation"),
);
cmd.image_barrier( render_graph::clear_pass(&mut rg, clear_color, framebuffer);
texture.image(),
vk::ImageAspectFlags::COLOR,
vk::PipelineStageFlags2::TRANSFER,
vk::AccessFlags2::empty(),
vk::PipelineStageFlags2::TRANSFER,
vk::AccessFlags2::TRANSFER_WRITE,
vk::ImageLayout::UNDEFINED,
vk::ImageLayout::TRANSFER_DST_OPTIMAL,
None,
);
cmd.copy_buffer_to_image(
staging.buffer(),
texture.image(),
vk::ImageLayout::TRANSFER_DST_OPTIMAL,
&[vk::BufferImageCopy {
buffer_offset: 0,
buffer_row_length: delta.image.width() as u32,
buffer_image_height: delta.image.height() as u32,
image_subresource: vk::ImageSubresourceLayers::default()
.aspect_mask(vk::ImageAspectFlags::COLOR)
.base_array_layer(0)
.mip_level(0)
.layer_count(1),
image_offset: vk::Offset3D { x: 0, y: 0, z: 0 },
image_extent: vk::Extent3D {
width: texture.size().width,
height: texture.size().height,
depth: 1,
},
}],
);
let id = self.egui_state.lookup_texture(*egui_id).unwrap_or_else(|| { let t = cb(self, &mut rg);
let id = texture::TextureId::new();
self.egui_state.textures.insert( let cmds = rg.resolve(dev.clone())?;
*egui_id,
EguiTextureInfo {
id,
options: delta.options,
},
);
id let future = cmds.submit(
}); Some((frame.acquire, vk::PipelineStageFlags::TRANSFER)),
Some(frame.release),
if let Some(pos) = delta.pos { Arc::new(sync::Fence::create(dev.clone())?),
// SAFETY: must exist because image is not whole.
let existing_texture = self.texture_handler.textures.get(&id).cloned().unwrap();
cmd.image_barrier(
texture.image(),
vk::ImageAspectFlags::COLOR,
vk::PipelineStageFlags2::TRANSFER,
vk::AccessFlags2::TRANSFER_WRITE,
vk::PipelineStageFlags2::TRANSFER,
vk::AccessFlags2::TRANSFER_READ,
vk::ImageLayout::TRANSFER_DST_OPTIMAL,
vk::ImageLayout::TRANSFER_SRC_OPTIMAL,
None,
);
cmd.image_barrier(
existing_texture.handle(),
vk::ImageAspectFlags::COLOR,
vk::PipelineStageFlags2::empty(),
vk::AccessFlags2::empty(),
vk::PipelineStageFlags2::TRANSFER,
vk::AccessFlags2::TRANSFER_WRITE,
vk::ImageLayout::UNDEFINED,
vk::ImageLayout::TRANSFER_DST_OPTIMAL,
None,
);
cmd.blit_images(
&texture,
util::Rect2D::new(0, 0, texture.width() as i32, texture.height() as i32),
&existing_texture,
util::Rect2D::new_from_size(
glam::ivec2(pos[0] as i32, pos[1] as i32),
glam::ivec2(texture.width() as i32, texture.height() as i32),
),
);
cmd.image_barrier(
existing_texture.handle(),
vk::ImageAspectFlags::COLOR,
vk::PipelineStageFlags2::TRANSFER,
vk::AccessFlags2::TRANSFER_WRITE,
vk::PipelineStageFlags2::FRAGMENT_SHADER,
vk::AccessFlags2::SHADER_SAMPLED_READ,
vk::ImageLayout::TRANSFER_DST_OPTIMAL,
vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
None,
);
} else {
cmd.image_barrier(
texture.handle(),
vk::ImageAspectFlags::COLOR,
vk::PipelineStageFlags2::TRANSFER,
vk::AccessFlags2::TRANSFER_WRITE,
vk::PipelineStageFlags2::FRAGMENT_SHADER,
vk::AccessFlags2::SHADER_SAMPLED_READ,
vk::ImageLayout::TRANSFER_DST_OPTIMAL,
vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
None,
);
self.texture_handler
.insert_image_with_id(id, texture.clone());
tracing::trace!("new texture for egui: {egui_id:?} -> {id:?}");
}
(staging, texture)
})
.collect::<Vec<_>>();
let draw_data = ctx.tessellate(output.shapes, output.pixels_per_point);
#[repr(C)]
#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
struct Vertex {
pos: glam::Vec2,
uv: glam::Vec2,
color: egui::epaint::Color32,
}
#[repr(transparent)]
#[derive(Debug, Clone, Copy)]
struct DrawCall(vk::DrawIndexedIndirectCommand);
unsafe impl bytemuck::Zeroable for DrawCall {}
unsafe impl bytemuck::Pod for DrawCall {}
let mut vertices = Vec::new();
let mut indices = Vec::new();
let mut draw_calls = Vec::new();
let mut textures = IndexMap::new();
let mut textures_indices = Vec::new();
for draw in draw_data {
let egui::epaint::Primitive::Mesh(mesh) = draw.primitive else {
continue;
};
draw_calls.push(DrawCall(vk::DrawIndexedIndirectCommand {
index_count: mesh.indices.len() as u32,
instance_count: 1,
first_index: indices.len() as u32,
vertex_offset: vertices.len() as i32,
first_instance: 0,
}));
vertices.extend(mesh.vertices.iter().map(|v| Vertex {
pos: glam::vec2(v.pos.x, v.pos.y),
uv: glam::vec2(v.uv.x, v.uv.y),
color: v.color,
}));
indices.extend(mesh.indices);
let texture = self
.egui_state
.textures
.get(&mesh.texture_id)
.cloned()
.unwrap();
if !textures.contains_key(&texture.id) {
textures.insert(texture.id, texture);
}
let idx = textures.get_index_of(&texture.id).unwrap();
textures_indices.push(idx as u32);
}
let num_draw_calls = draw_calls.len();
let device = self.vulkan.device.clone();
let (draw_staging, vertices, indices, draw_calls, texture_ids) = {
let vertices_size = vertices.len() * size_of::<Vertex>();
let indices_size = indices.len() * size_of::<u32>();
let draw_calls_size = draw_calls.len() * size_of::<vk::DrawIndexedIndirectCommand>();
let staging_size = vertices_size + indices_size + draw_calls_size;
let mut staging = buffers::Buffer::new(
device.clone(),
buffers::BufferDesc {
name: Some("egui-draw-staging".into()),
size: staging_size as u64,
usage: vk::BufferUsageFlags::TRANSFER_SRC,
mem_usage: vk_mem::MemoryUsage::AutoPreferHost,
alloc_flags: vk_mem::AllocationCreateFlags::MAPPED
| vk_mem::AllocationCreateFlags::HOST_ACCESS_SEQUENTIAL_WRITE
| vk_mem::AllocationCreateFlags::STRATEGY_FIRST_FIT,
..Default::default()
},
)?; )?;
{ // call pre_present_notify
let mut map = staging.map()?; pre_present_cb();
let (st_vertices, rest) = map.split_at_mut(vertices_size); future.await;
let (st_indices, st_drawcalls) = rest.split_at_mut(indices_size); let wait = Some(frame.release);
st_vertices.copy_from_slice(bytemuck::cast_slice(&vertices)); frame.present(wait)?;
st_indices.copy_from_slice(bytemuck::cast_slice(&indices));
st_drawcalls.copy_from_slice(bytemuck::cast_slice(&draw_calls));
}
let vertices = buffers::Buffer::new( Ok(t)
device.clone(),
buffers::BufferDesc {
name: Some("egui-draw-vertices".into()),
size: vertices_size as u64,
usage: vk::BufferUsageFlags::TRANSFER_DST | vk::BufferUsageFlags::VERTEX_BUFFER,
mem_usage: vk_mem::MemoryUsage::AutoPreferDevice,
..Default::default()
},
)?;
let indices = buffers::Buffer::new(
device.clone(),
buffers::BufferDesc {
name: Some("egui-draw-indices".into()),
size: indices_size as u64,
usage: vk::BufferUsageFlags::TRANSFER_DST | vk::BufferUsageFlags::INDEX_BUFFER,
mem_usage: vk_mem::MemoryUsage::AutoPreferDevice,
..Default::default()
},
)?;
let draw_calls = buffers::Buffer::new(
device.clone(),
buffers::BufferDesc {
name: Some("egui-draw-draw_calls".into()),
size: draw_calls_size as u64,
usage: vk::BufferUsageFlags::TRANSFER_DST
| vk::BufferUsageFlags::INDIRECT_BUFFER,
mem_usage: vk_mem::MemoryUsage::AutoPreferDevice,
..Default::default()
},
)?;
cmd.copy_buffers(
staging.buffer(),
vertices.buffer(),
&[vk::BufferCopy {
src_offset: 0,
dst_offset: 0,
size: vertices_size as u64,
}],
);
cmd.copy_buffers(
staging.buffer(),
indices.buffer(),
&[vk::BufferCopy {
src_offset: vertices_size as u64,
dst_offset: 0,
size: indices_size as u64,
}],
);
cmd.copy_buffers(
staging.buffer(),
draw_calls.buffer(),
&[vk::BufferCopy {
src_offset: (vertices_size + indices_size) as u64,
dst_offset: 0,
size: draw_calls_size as u64,
}],
);
let mut texture_ids = buffers::Buffer::new(
device.clone(),
buffers::BufferDesc {
name: Some("egui-draw-texture_ids".into()),
size: (textures_indices.len() * size_of::<u32>()) as u64,
usage: vk::BufferUsageFlags::STORAGE_BUFFER,
mem_usage: vk_mem::MemoryUsage::AutoPreferDevice,
alloc_flags: vk_mem::AllocationCreateFlags::HOST_ACCESS_SEQUENTIAL_WRITE,
..Default::default()
},
)?;
{
let mut map = texture_ids.map()?;
map.copy_from_slice(bytemuck::cast_slice(&textures_indices));
}
(staging, vertices, indices, draw_calls, texture_ids)
};
let descriptor_infos = textures
.values()
.map(|entry| {
let texture = self.texture_handler.get_texture(entry.id).unwrap();
let info = vk::DescriptorImageInfo {
sampler: self
.vulkan
.samplers
.get_sampler(entry.into_sampler_desc())
.unwrap(),
image_view: texture
.get_view(images::ImageViewDesc {
kind: vk::ImageViewType::TYPE_2D,
format: texture.format(),
aspect: vk::ImageAspectFlags::COLOR,
mip_range: (0..1).into(),
layer_range: (0..1).into(),
..Default::default()
})
.unwrap(),
image_layout: vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
};
info
})
.collect::<Vec<_>>();
let uniform_info = vk::DescriptorBufferInfo {
buffer: texture_ids.buffer(),
offset: 0,
range: texture_ids.len(),
};
let descriptor_writes = descriptor_infos
.iter()
.enumerate()
.map(|(i, info)| {
vk::WriteDescriptorSet::default()
.image_info(core::slice::from_ref(info))
.descriptor_count(1)
.descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
.dst_set(self.egui_state.descriptor_set)
.dst_binding(EguiState::TEXTURE_BINDING)
.dst_array_element(i as u32)
})
.chain(core::iter::once({
vk::WriteDescriptorSet::default()
.buffer_info(core::slice::from_ref(&uniform_info))
.descriptor_count(1)
.dst_binding(EguiState::UNIFORM_BINDING)
.descriptor_type(vk::DescriptorType::STORAGE_BUFFER)
.dst_array_element(0)
.dst_set(self.egui_state.descriptor_set)
}))
.collect::<Vec<_>>();
unsafe {
device.dev().update_descriptor_sets(&descriptor_writes, &[]);
}
let to_remove_tex_ids = output
.textures_delta
.free
.iter()
.filter_map(|id| self.egui_state.textures.get(id).cloned())
.map(|entry| entry.id)
.collect::<Vec<_>>();
self.egui_state.render_state = Some(EguiRenderState {
vertices,
indices,
draw_calls,
num_draw_calls,
texture_ids,
textures_to_free: to_remove_tex_ids,
});
let fence = Arc::new(sync::Fence::create(device.clone()).unwrap());
let future = cmd.submit_async(None, None, fence).unwrap();
future.block()?;
black_box((cmd_objects, draw_staging));
// free after drawing
Ok(())
} }
pub fn debug_draw_egui<K, F>( pub fn debug_draw_egui<K, F>(
@ -2588,159 +2209,6 @@ impl<W> Renderer<W> {
Ok(()) Ok(())
} }
pub fn debug_draw<K, F: FnOnce()>(&mut self, window: &K, pre_present_cb: F) -> Result<()>
where
K: core::hash::Hash + Eq,
W: core::hash::Hash + Eq + Borrow<K>,
{
let dev = self.vulkan.device.clone();
let pool = commands::SingleUseCommandPool::new(dev.clone(), dev.graphics_queue().clone())?;
if let Some(ctx) = self.window_contexts.get(window) {
let (frame, suboptimal) =
smol::block_on(ctx.current_swapchain.read().clone().acquire_image())?;
if suboptimal {
tracing::warn!(
"swapchain ({:?}) is suboptimal!",
ctx.current_swapchain.read().swapchain
);
}
let [r, g, b] = rand::prelude::StdRng::seed_from_u64(ctx.surface.surface.as_raw())
.gen::<[f32; 3]>();
let clear_color = Rgba([r, g, b, 1.0]);
let egui_ctx = self.egui_state.render_state.take();
let cmd = util::timed("record command buffer", || {
let cmd = pool.alloc()?;
cmd.image_barrier(
frame.image.handle(),
vk::ImageAspectFlags::COLOR,
vk::PipelineStageFlags2::TRANSFER,
vk::AccessFlags2::empty(),
vk::PipelineStageFlags2::TRANSFER,
vk::AccessFlags2::TRANSFER_WRITE,
vk::ImageLayout::UNDEFINED,
vk::ImageLayout::TRANSFER_DST_OPTIMAL,
None,
);
cmd.clear_color_image(
frame.image.handle(),
frame.format,
vk::ImageLayout::TRANSFER_DST_OPTIMAL,
clear_color,
&[images::SUBRESOURCERANGE_COLOR_ALL],
);
cmd.image_barrier(
frame.image.handle(),
vk::ImageAspectFlags::COLOR,
vk::PipelineStageFlags2::TRANSFER,
vk::AccessFlags2::TRANSFER_WRITE,
vk::PipelineStageFlags2::FRAGMENT_SHADER,
vk::AccessFlags2::SHADER_WRITE,
vk::ImageLayout::TRANSFER_DST_OPTIMAL,
vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL,
None,
);
if let Some(ctx) = egui_ctx.as_ref() {
_ = &ctx.texture_ids;
let color_attachment = &vk::RenderingAttachmentInfo::default()
.image_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL)
.image_view(frame.view)
.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(frame.swapchain.extent)),
);
cmd.set_scissors(&[vk::Rect2D::default()
.offset(vk::Offset2D::default())
.extent(frame.swapchain.extent)]);
cmd.set_viewport(&[vk::Viewport::default()
.x(0.0)
.y(0.0)
.min_depth(0.0)
.max_depth(1.0)
.width(frame.swapchain.extent.width as f32)
.height(frame.swapchain.extent.height as f32)]);
cmd.bind_pipeline(&self.egui_state.pipeline);
cmd.bind_indices(ctx.indices.buffer(), 0, vk::IndexType::UINT32);
cmd.bind_vertices(ctx.vertices.buffer(), 0);
cmd.push_constants(
&self.egui_state.pipeline_layout,
vk::ShaderStageFlags::VERTEX,
0,
bytemuck::cast_slice(
&[
frame.swapchain.extent.width as f32,
frame.swapchain.extent.height as f32,
]
.map(|f| f.to_bits()),
),
);
cmd.bind_descriptor_sets(
&self.egui_state.pipeline_layout,
vk::PipelineBindPoint::GRAPHICS,
&[self.egui_state.descriptor_set],
);
cmd.draw_indexed_indirect(
ctx.draw_calls.buffer(),
0,
ctx.num_draw_calls as u32,
size_of::<vk::DrawIndexedIndirectCommand>() as u32,
);
cmd.end_rendering();
}
cmd.image_barrier(
frame.image.handle(),
vk::ImageAspectFlags::COLOR,
vk::PipelineStageFlags2::FRAGMENT_SHADER,
vk::AccessFlags2::SHADER_WRITE,
vk::PipelineStageFlags2::BOTTOM_OF_PIPE,
vk::AccessFlags2::empty(),
vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL,
vk::ImageLayout::PRESENT_SRC_KHR,
None,
);
Result::Ok(cmd)
})?;
let future = cmd.submit_async(
Some((frame.acquire, vk::PipelineStageFlags::TRANSFER)),
Some(frame.release),
Arc::new(sync::Fence::create(dev.clone())?),
)?;
// call pre_present_notify
pre_present_cb();
let wait = Some(frame.release);
frame.present(wait)?;
future.block()?;
egui_ctx.map(|ctx| {
for id in ctx.textures_to_free {
self.texture_handler.remove_texture(id);
}
});
}
Ok(())
}
pub fn new_window_context( pub fn new_window_context(
&mut self, &mut self,
extent: vk::Extent2D, extent: vk::Extent2D,

View file

@ -43,7 +43,7 @@ impl From<GraphResourceDesc> for GraphResource {
#[derive(Default, Debug, PartialEq, Eq)] #[derive(Default, Debug, PartialEq, Eq)]
pub enum GraphResource { pub enum GraphResource {
Framebuffer(Arc<SwapchainFrame>), Framebuffer(Arc<Image>),
ImportedImage(Arc<Image>), ImportedImage(Arc<Image>),
ImportedBuffer(Arc<Buffer>), ImportedBuffer(Arc<Buffer>),
Image(Arc<Image>), Image(Arc<Image>),
@ -63,7 +63,7 @@ impl GraphResource {
match self { match self {
GraphResource::Framebuffer(swapchain_frame) => { GraphResource::Framebuffer(swapchain_frame) => {
(swapchain_frame.index, swapchain_frame.image.handle()).hash(&mut state) (swapchain_frame.handle()).hash(&mut state)
} }
GraphResource::ImportedImage(image) => image.handle().hash(&mut state), GraphResource::ImportedImage(image) => image.handle().hash(&mut state),
GraphResource::ImportedBuffer(buffer) => buffer.handle().hash(&mut state), GraphResource::ImportedBuffer(buffer) => buffer.handle().hash(&mut state),
@ -95,6 +95,7 @@ pub struct RenderContext<'a> {
pub device: device::Device, pub device: device::Device,
pub cmd: commands::SingleUseCommand, pub cmd: commands::SingleUseCommand,
pub resources: &'a [GraphResource], pub resources: &'a [GraphResource],
pub framebuffer: Option<GraphResourceId>,
} }
impl RenderContext<'_> { impl RenderContext<'_> {
@ -102,7 +103,7 @@ impl RenderContext<'_> {
self.resources.get(id.0 as usize).and_then(|res| match res { self.resources.get(id.0 as usize).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), GraphResource::Framebuffer(fb) => Some(fb),
_ => None, _ => None,
}) })
} }
@ -113,6 +114,14 @@ impl RenderContext<'_> {
_ => None, _ => None,
}) })
} }
pub fn get_framebuffer(&self) -> Option<&Image> {
self.framebuffer
.and_then(|rid| self.resources.get(rid.0 as usize))
.and_then(|res| match res {
GraphResource::Framebuffer(arc) => Some(arc.as_ref()),
_ => None,
})
}
} }
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord)]
@ -349,6 +358,7 @@ pub struct RenderGraph {
/// the rendergraph produces these resources. Any passes on which these /// the rendergraph produces these resources. Any passes on which these
/// outputs do not depend are pruned. /// outputs do not depend are pruned.
outputs: BTreeMap<GraphResourceId, Access>, outputs: BTreeMap<GraphResourceId, Access>,
pub(crate) framebuffer: Option<GraphResourceId>,
} }
impl RenderGraph { impl RenderGraph {
@ -360,9 +370,14 @@ impl RenderGraph {
resources: Vec::new(), resources: Vec::new(),
pass_descs, pass_descs,
outputs: BTreeMap::new(), outputs: BTreeMap::new(),
framebuffer: None,
} }
} }
pub fn get_framebuffer(&self) -> Option<GraphResourceId> {
self.framebuffer
}
fn get_next_resource_id(&mut self) -> GraphResourceId { fn get_next_resource_id(&mut self) -> GraphResourceId {
GraphResourceId(self.resources.len() as u32) GraphResourceId(self.resources.len() as u32)
} }
@ -406,8 +421,14 @@ impl RenderGraph {
self.import_resource(res, access) self.import_resource(res, access)
} }
pub fn import_framebuffer(&mut self, frame: Arc<SwapchainFrame>) -> GraphResourceId { pub fn import_framebuffer(&mut self, frame: &SwapchainFrame) -> GraphResourceId {
self.import_resource(GraphResource::Framebuffer(frame), Access::undefined()) let rid = self.import_resource(
GraphResource::Framebuffer(frame.image.clone()),
Access::undefined(),
);
self.mark_as_output(rid, Access::present());
self.framebuffer = Some(rid);
rid
} }
pub fn add_pass(&mut self, pass: PassDesc) { pub fn add_pass(&mut self, pass: PassDesc) {
@ -500,6 +521,7 @@ impl RenderGraph {
device: device.clone(), device: device.clone(),
cmd, cmd,
resources, resources,
framebuffer: self.framebuffer,
}; };
for pass in passes { for pass in passes {
@ -538,7 +560,7 @@ impl RenderGraph {
) { ) {
let barrier: Barrier = match res { let barrier: Barrier = match res {
GraphResource::Framebuffer(arc) => { GraphResource::Framebuffer(arc) => {
image_barrier(arc.image.handle(), arc.image.format(), from, to, None).into() image_barrier(arc.handle(), arc.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()

View file

@ -1,6 +1,7 @@
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use ash::vk; use ash::vk;
use bytemuck::{Pod, Zeroable};
#[macro_export] #[macro_export]
macro_rules! def_monotonic_id { macro_rules! def_monotonic_id {
@ -350,8 +351,13 @@ pub fn timed<T, F: FnOnce() -> T>(label: &str, f: F) -> T {
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
#[repr(transparent)]
pub struct Rgba(pub [f32; 4]); pub struct Rgba(pub [f32; 4]);
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
#[repr(transparent)]
pub struct Rgba8(pub [u8; 4]);
impl std::hash::Hash for Rgba { impl std::hash::Hash for Rgba {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) { fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.map(|f| hash_f32(state, f)); self.0.map(|f| hash_f32(state, f));
@ -369,6 +375,9 @@ impl Rgba {
pub fn into_f32(&self) -> [f32; 4] { pub fn into_f32(&self) -> [f32; 4] {
self.0 self.0
} }
pub fn into_u8(&self) -> Rgba8 {
Rgba8(self.0.map(|f| (f.clamp(0.0, 1.0) * 255.0) as u8))
}
pub fn into_snorm(&self) -> [f32; 4] { pub fn into_snorm(&self) -> [f32; 4] {
self.0.map(|f| (f - 0.5) * 2.0) self.0.map(|f| (f - 0.5) * 2.0)
} }