Luna: add command impls, simple wireframe demo
This commit is contained in:
parent
cb02051cf3
commit
ab0812ff72
|
|
@ -31,6 +31,8 @@ struct WinitState {
|
|||
renderer: Renderer2,
|
||||
last_redraw: std::time::Instant,
|
||||
egui_state: renderer::EguiState,
|
||||
wireframe: Option<renderer::rendering::Wireframe>,
|
||||
typed_graph: bool,
|
||||
}
|
||||
|
||||
impl WinitState {
|
||||
|
|
@ -38,6 +40,29 @@ impl WinitState {
|
|||
const BASE_HEIGHT: u32 = 600;
|
||||
fn new(window_title: String, display: DisplayHandle) -> WinitState {
|
||||
let renderer = Renderer2::new(display.as_raw()).expect("renderer");
|
||||
let typed_graph = std::env::var_os("VIDYA_TYPED_GRAPH").is_some();
|
||||
let wireframe = typed_graph.then(|| {
|
||||
smol::block_on(renderer::rendering::Wireframe::from_triangles_indexed(
|
||||
renderer.device().clone(),
|
||||
vec![
|
||||
glam::vec3(-1.5, -1.0, 0.0),
|
||||
glam::vec3(1.5, -1.0, 0.0),
|
||||
glam::vec3(0.0, 1.5, 0.0),
|
||||
],
|
||||
vec![0, 1, 2],
|
||||
vec![
|
||||
renderer::util::Rgba8([255, 80, 80, 255]),
|
||||
renderer::util::Rgba8([80, 255, 120, 255]),
|
||||
renderer::util::Rgba8([80, 160, 255, 255]),
|
||||
],
|
||||
))
|
||||
.expect("wireframe scene")
|
||||
});
|
||||
if typed_graph {
|
||||
tracing::warn!(
|
||||
"VIDYA_TYPED_GRAPH enabled; egui is not yet supported on the typed graph"
|
||||
);
|
||||
}
|
||||
|
||||
Self {
|
||||
windows: BTreeMap::new(),
|
||||
|
|
@ -49,6 +74,8 @@ impl WinitState {
|
|||
// TODO: pass down this error and add some kind of error handling UI or dump
|
||||
egui_state: renderer::EguiState::new(renderer.device().clone()).unwrap(),
|
||||
renderer,
|
||||
wireframe,
|
||||
typed_graph,
|
||||
last_redraw: std::time::Instant::now(),
|
||||
}
|
||||
}
|
||||
|
|
@ -99,6 +126,20 @@ impl WinitState {
|
|||
.filter_map(|id| self.egui_state.lookup_texture(*id))
|
||||
.collect::<Vec<_>>();
|
||||
// rendering
|
||||
let render_result = if self.typed_graph {
|
||||
let wireframe = self
|
||||
.wireframe
|
||||
.as_ref()
|
||||
.expect("typed graph wireframe initialized");
|
||||
smol::block_on(self.renderer.draw_graph_typed(
|
||||
&window.surface,
|
||||
move |_renderer, builder, target, extent, list| {
|
||||
wireframe.typed_pass(builder, target, extent, list);
|
||||
renderer::Result::Ok(())
|
||||
},
|
||||
))
|
||||
.and_then(|result| result)
|
||||
} else {
|
||||
let render = self.renderer.draw_graph(&window.surface, |renderer, rg| {
|
||||
use renderer::rendering::{egui_pass, egui_pre_pass};
|
||||
let framebuffer = rg.get_framebuffer().unwrap();
|
||||
|
|
@ -130,8 +171,10 @@ impl WinitState {
|
|||
|
||||
renderer::Result::Ok(())
|
||||
});
|
||||
smol::block_on(render).flatten()
|
||||
};
|
||||
|
||||
match smol::block_on(render).flatten() {
|
||||
match render_result {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
tracing::error!("encountered error while rendering: {e}");
|
||||
|
|
|
|||
|
|
@ -335,7 +335,13 @@ pub mod traits {
|
|||
|
||||
tracing::trace!("submitted queue {:?} and fence {:?}", queue, fence);
|
||||
|
||||
Ok(crate::sync::FenceFuture::new(fence))
|
||||
let future = crate::sync::FenceFuture::new(fence);
|
||||
Ok(async move {
|
||||
future.await;
|
||||
// Keep the command buffer allocated until the submitted work
|
||||
// has completed; it is only safe to return it to the pool then.
|
||||
drop(self);
|
||||
})
|
||||
}
|
||||
|
||||
fn image_barrier(
|
||||
|
|
@ -621,7 +627,9 @@ pub mod traits {
|
|||
}
|
||||
}
|
||||
|
||||
pub use command_pools::{CommandBuffer, CommandBufferFuture, CommandPool, CommandPools};
|
||||
pub use command_pools::{
|
||||
CommandBuffer, CommandBufferFuture, CommandPool, CommandPools, Error as CommandPoolError,
|
||||
};
|
||||
|
||||
mod command_pools {
|
||||
use std::{borrow::Cow, cell::Cell, mem::ManuallyDrop, sync::Arc};
|
||||
|
|
@ -689,11 +697,13 @@ mod command_pools {
|
|||
|
||||
pub fn free_command_buffers(&self) {
|
||||
self.free_list_mut(|levels, buffers| {
|
||||
if !buffers.is_empty() {
|
||||
unsafe {
|
||||
self.device()
|
||||
.dev()
|
||||
.free_command_buffers(self.handle(), &buffers);
|
||||
}
|
||||
}
|
||||
levels.clear();
|
||||
buffers.clear();
|
||||
});
|
||||
|
|
@ -829,6 +839,13 @@ mod command_pools {
|
|||
}
|
||||
|
||||
impl CommandPools {
|
||||
pub fn new(device: Device) -> Self {
|
||||
Self {
|
||||
device,
|
||||
shards: ThreadLocal::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_for_family(&self, family: u32) -> Arc<CommandPool> {
|
||||
let shard = self
|
||||
.shards
|
||||
|
|
|
|||
|
|
@ -184,6 +184,10 @@ pub struct Image {
|
|||
}
|
||||
|
||||
impl Image {
|
||||
pub(crate) fn desc(&self) -> &ImageDesc {
|
||||
&self.desc
|
||||
}
|
||||
|
||||
pub fn new(device: Device, desc: ImageDesc) -> crate::Result<Self> {
|
||||
let (image, requirements) = Self::new_raw(device.clone(), &desc)?;
|
||||
let alloc = device.alloc2.lock().allocate(&AllocationCreateDesc {
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ pub mod sync;
|
|||
mod text;
|
||||
pub mod util;
|
||||
|
||||
use commands::traits::CommandBufferExt;
|
||||
use device::Device;
|
||||
|
||||
mod texture {
|
||||
|
|
@ -91,6 +92,8 @@ pub enum Error {
|
|||
#[error(transparent)]
|
||||
Result(#[from] ash::vk::Result),
|
||||
#[error(transparent)]
|
||||
CommandPool(#[from] commands::CommandPoolError),
|
||||
#[error(transparent)]
|
||||
CStrError(#[from] core::ffi::c_str::FromBytesUntilNulError),
|
||||
#[error(transparent)]
|
||||
Utf8Error(#[from] core::str::Utf8Error),
|
||||
|
|
@ -925,6 +928,7 @@ impl EguiState {
|
|||
|
||||
pub struct Renderer2 {
|
||||
pub device: Device,
|
||||
command_pools: commands::CommandPools,
|
||||
pub samplers: SamplerCache,
|
||||
pub texture_manager: texture::TextureManager,
|
||||
|
||||
|
|
@ -946,6 +950,7 @@ impl Renderer2 {
|
|||
PhysicalDeviceFeatures {
|
||||
core: vk::PhysicalDeviceFeatures {
|
||||
multi_draw_indirect: vk::TRUE,
|
||||
fill_mode_non_solid: vk::TRUE,
|
||||
..Default::default()
|
||||
},
|
||||
core11: vk::PhysicalDeviceVulkan11Features {
|
||||
|
|
@ -970,6 +975,7 @@ impl Renderer2 {
|
|||
)?;
|
||||
|
||||
Ok(Self {
|
||||
command_pools: commands::CommandPools::new(device.clone()),
|
||||
samplers: SamplerCache::new(device.clone()),
|
||||
texture_manager: texture::TextureManager::new(device.clone()),
|
||||
display,
|
||||
|
|
@ -1047,6 +1053,79 @@ impl Renderer2 {
|
|||
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
pub async fn draw_graph_typed<T, F>(&mut self, surface: &swapchain::Surface, cb: F) -> Result<T>
|
||||
where
|
||||
F: FnOnce(
|
||||
&mut Renderer2,
|
||||
&mut render_graph::GraphBuilder<'_>,
|
||||
render_graph::Texture,
|
||||
(u32, u32),
|
||||
&mut render_graph::CommandList<'_>,
|
||||
) -> T,
|
||||
{
|
||||
let Some(frame) = surface.acquire_image() else {
|
||||
return Err(Error::SuboptimalSwapchain);
|
||||
};
|
||||
let (frame, _suboptimal) = frame.await?;
|
||||
|
||||
let mut builder = render_graph::GraphBuilder::new(self.device.clone());
|
||||
let target = builder.import_image(frame.image().clone());
|
||||
let target_id = target.0;
|
||||
let image_extent = frame.image().desc().extent;
|
||||
let extent = (image_extent.width, image_extent.height);
|
||||
|
||||
let mut list = builder.make_command_list();
|
||||
// Seed the folder with UNDEFINED so BeginRendering emits the initial
|
||||
// transition; CLEAR intentionally discards the image's previous contents.
|
||||
list.push(render_graph::ImportResource {
|
||||
resource: render_graph::Texture(target_id),
|
||||
access: vk::AccessFlags2::NONE,
|
||||
stage: vk::PipelineStageFlags2::NONE,
|
||||
layout: Some(vk::ImageLayout::UNDEFINED),
|
||||
});
|
||||
let out = cb(self, &mut builder, target, extent, &mut list);
|
||||
list.push(render_graph::PreparePresent {
|
||||
texture: render_graph::TextureRegion {
|
||||
texture: render_graph::Texture(target_id),
|
||||
range: render_graph::TextureRange {
|
||||
aspect: vk::ImageAspectFlags::COLOR,
|
||||
mip_levels: (0..1).into(),
|
||||
array_layers: (0..1).into(),
|
||||
origin: (0, 0, 0),
|
||||
extent: (extent.0, extent.1, 1),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
let queue = self.device.queues().graphics();
|
||||
let command_buffer = self.command_pools.allocate_buffer(
|
||||
queue.family_index(),
|
||||
Some("typed-frame".into()),
|
||||
vk::CommandBufferLevel::PRIMARY,
|
||||
vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT,
|
||||
)?;
|
||||
let registry = builder.registry();
|
||||
let mut recorder = render_graph::CommandRecorder::new(command_buffer, registry);
|
||||
list.record_commands(&mut recorder);
|
||||
drop(list);
|
||||
let command_buffer = recorder.into_command_buffer();
|
||||
let future = command_buffer.submit_to_queue(
|
||||
queue,
|
||||
Some((
|
||||
frame.acquire,
|
||||
vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT,
|
||||
)),
|
||||
Some(frame.release),
|
||||
Arc::new(sync::Fence::from_pool(&self.device.pools.fences, None)?),
|
||||
)?;
|
||||
|
||||
future.await;
|
||||
let release = frame.release;
|
||||
frame.present(Some(release))?;
|
||||
|
||||
Ok(out)
|
||||
}
|
||||
}
|
||||
|
||||
pub use vk::Extent2D;
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ use crate::{
|
|||
device::DeviceOwned,
|
||||
render_graph::recorder::{CommandRecorder, SideEffectMap},
|
||||
};
|
||||
use ash_formats::{FormatExt as _, FormatHelpers as _};
|
||||
|
||||
use super::resources::*;
|
||||
|
||||
|
|
@ -36,7 +37,10 @@ impl<T: Resource> Command for ImportResource<T> {
|
|||
));
|
||||
}
|
||||
|
||||
fn apply(self, _recorder: &mut CommandRecorder) {
|
||||
fn apply(self, recorder: &mut CommandRecorder) {
|
||||
if let Some(layout) = self.layout {
|
||||
recorder.set_image_layout(self.resource.id(), layout);
|
||||
}
|
||||
// No actual GPU commands needed for import, as it's just a logical operation
|
||||
}
|
||||
}
|
||||
|
|
@ -72,7 +76,7 @@ impl<T: Resource, U: Resource> Copy<T, U> {
|
|||
vk::AccessFlags2::TRANSFER_READ,
|
||||
Some(vk::ImageLayout::TRANSFER_SRC_OPTIMAL),
|
||||
));
|
||||
self.src.side_effect(map.reborrow().into_side_effect_map2(
|
||||
self.dst.side_effect(map.reborrow().into_side_effect_map2(
|
||||
vk::PipelineStageFlags2::TRANSFER,
|
||||
vk::AccessFlags2::TRANSFER_WRITE,
|
||||
Some(vk::ImageLayout::TRANSFER_DST_OPTIMAL),
|
||||
|
|
@ -277,7 +281,28 @@ impl Command for ClearTexture {
|
|||
));
|
||||
}
|
||||
|
||||
fn apply(self, _recorder: &mut CommandRecorder) {}
|
||||
fn apply(self, recorder: &mut CommandRecorder) {
|
||||
let cmd = recorder.cmd_buffer();
|
||||
let format = recorder.get_image_desc(self.dst.id()).format;
|
||||
debug_assert!(format.aspect_flags().contains(vk::ImageAspectFlags::COLOR));
|
||||
let clear_value = format.clear_value(self.clear_value);
|
||||
let range = self.dst.range;
|
||||
let subresource = vk::ImageSubresourceRange::default()
|
||||
.aspect_mask(range.aspect & format.aspect_flags())
|
||||
.base_mip_level(range.mip_levels.start())
|
||||
.level_count(range.mip_levels.len())
|
||||
.base_array_layer(range.array_layers.start())
|
||||
.layer_count(range.array_layers.len());
|
||||
unsafe {
|
||||
cmd.device().raw.cmd_clear_color_image(
|
||||
cmd.raw(),
|
||||
recorder.get_image_handle(self.dst.id()),
|
||||
vk::ImageLayout::TRANSFER_DST_OPTIMAL,
|
||||
&clear_value,
|
||||
&[subresource],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UpdateBuffer {
|
||||
|
|
@ -294,17 +319,37 @@ impl Command for UpdateBuffer {
|
|||
));
|
||||
}
|
||||
|
||||
fn apply(self, _recorder: &mut CommandRecorder) {}
|
||||
fn apply(self, recorder: &mut CommandRecorder) {
|
||||
let cmd = recorder.cmd_buffer();
|
||||
let mut data = self.data;
|
||||
let padded_len = data.len().div_ceil(4) * 4;
|
||||
data.resize(padded_len, 0);
|
||||
unsafe {
|
||||
cmd.device().raw.cmd_update_buffer(
|
||||
cmd.raw(),
|
||||
recorder.get_buffer_handle(self.dst.id()),
|
||||
self.dst.range.offset,
|
||||
&data,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BeginRendering {
|
||||
pub color_attachments: Vec<TextureRegion>,
|
||||
pub depth_attachment: Option<TextureRegion>,
|
||||
pub stencil_attachment: Option<TextureRegion>,
|
||||
pub color_attachments: Vec<RenderingAttachment>,
|
||||
pub depth_attachment: Option<RenderingAttachment>,
|
||||
pub stencil_attachment: Option<RenderingAttachment>,
|
||||
pub area: (u32, u32),
|
||||
pub layers: u32,
|
||||
}
|
||||
|
||||
pub struct RenderingAttachment {
|
||||
pub region: TextureRegion,
|
||||
pub load_op: vk::AttachmentLoadOp,
|
||||
pub store_op: vk::AttachmentStoreOp,
|
||||
pub clear_value: vk::ClearValue,
|
||||
}
|
||||
|
||||
impl Command for BeginRendering {
|
||||
fn side_effects(&self, mut map: SideEffectMap) {
|
||||
for attachment in &self.color_attachments {
|
||||
|
|
@ -313,30 +358,107 @@ impl Command for BeginRendering {
|
|||
// pass instance referencing framebuffer-local stages must contain
|
||||
// only framebuffer-local stages.
|
||||
|
||||
// TODO: consider loadop and storeop?
|
||||
attachment.side_effect(map.reborrow().into_side_effect_map2(
|
||||
attachment
|
||||
.region
|
||||
.side_effect(map.reborrow().into_side_effect_map2(
|
||||
vk::PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT,
|
||||
vk::AccessFlags2::COLOR_ATTACHMENT_READ,
|
||||
vk::AccessFlags2::COLOR_ATTACHMENT_WRITE
|
||||
| if attachment.load_op == vk::AttachmentLoadOp::LOAD {
|
||||
vk::AccessFlags2::COLOR_ATTACHMENT_READ
|
||||
} else {
|
||||
vk::AccessFlags2::empty()
|
||||
},
|
||||
Some(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL),
|
||||
));
|
||||
}
|
||||
if let Some(depth) = &self.depth_attachment {
|
||||
depth.side_effect(map.reborrow().into_side_effect_map2(
|
||||
vk::PipelineStageFlags2::EARLY_FRAGMENT_TESTS,
|
||||
vk::AccessFlags2::DEPTH_STENCIL_ATTACHMENT_READ,
|
||||
depth
|
||||
.region
|
||||
.side_effect(map.reborrow().into_side_effect_map2(
|
||||
vk::PipelineStageFlags2::EARLY_FRAGMENT_TESTS
|
||||
| vk::PipelineStageFlags2::LATE_FRAGMENT_TESTS,
|
||||
vk::AccessFlags2::DEPTH_STENCIL_ATTACHMENT_WRITE
|
||||
| if depth.load_op == vk::AttachmentLoadOp::LOAD {
|
||||
vk::AccessFlags2::DEPTH_STENCIL_ATTACHMENT_READ
|
||||
} else {
|
||||
vk::AccessFlags2::empty()
|
||||
},
|
||||
Some(vk::ImageLayout::DEPTH_ATTACHMENT_OPTIMAL),
|
||||
));
|
||||
}
|
||||
if let Some(stencil) = &self.stencil_attachment {
|
||||
stencil.side_effect(map.reborrow().into_side_effect_map2(
|
||||
vk::PipelineStageFlags2::EARLY_FRAGMENT_TESTS,
|
||||
vk::AccessFlags2::DEPTH_STENCIL_ATTACHMENT_READ,
|
||||
stencil
|
||||
.region
|
||||
.side_effect(map.reborrow().into_side_effect_map2(
|
||||
vk::PipelineStageFlags2::EARLY_FRAGMENT_TESTS
|
||||
| vk::PipelineStageFlags2::LATE_FRAGMENT_TESTS,
|
||||
vk::AccessFlags2::DEPTH_STENCIL_ATTACHMENT_WRITE
|
||||
| if stencil.load_op == vk::AttachmentLoadOp::LOAD {
|
||||
vk::AccessFlags2::DEPTH_STENCIL_ATTACHMENT_READ
|
||||
} else {
|
||||
vk::AccessFlags2::empty()
|
||||
},
|
||||
Some(vk::ImageLayout::STENCIL_ATTACHMENT_OPTIMAL),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
fn apply(self, _recorder: &mut CommandRecorder) {}
|
||||
fn apply(self, recorder: &mut CommandRecorder) {
|
||||
let cmd = recorder.cmd_buffer();
|
||||
let color_attachments = self
|
||||
.color_attachments
|
||||
.iter()
|
||||
.map(|attachment| {
|
||||
vk::RenderingAttachmentInfo::default()
|
||||
.image_view(recorder.get_image_view_handle(attachment.region.id()))
|
||||
.image_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL)
|
||||
.load_op(attachment.load_op)
|
||||
.store_op(attachment.store_op)
|
||||
.clear_value(attachment.clear_value)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let depth_attachment = self.depth_attachment.as_ref().map(|attachment| {
|
||||
vk::RenderingAttachmentInfo::default()
|
||||
.image_view(recorder.get_image_view_handle(attachment.region.id()))
|
||||
.image_layout(vk::ImageLayout::DEPTH_ATTACHMENT_OPTIMAL)
|
||||
.load_op(attachment.load_op)
|
||||
.store_op(attachment.store_op)
|
||||
.clear_value(attachment.clear_value)
|
||||
});
|
||||
let stencil_attachment = self.stencil_attachment.as_ref().map(|attachment| {
|
||||
vk::RenderingAttachmentInfo::default()
|
||||
.image_view(recorder.get_image_view_handle(attachment.region.id()))
|
||||
.image_layout(vk::ImageLayout::STENCIL_ATTACHMENT_OPTIMAL)
|
||||
.load_op(attachment.load_op)
|
||||
.store_op(attachment.store_op)
|
||||
.clear_value(attachment.clear_value)
|
||||
});
|
||||
let rendering_info = vk::RenderingInfo::default()
|
||||
.render_area(vk::Rect2D {
|
||||
offset: vk::Offset2D { x: 0, y: 0 },
|
||||
extent: vk::Extent2D {
|
||||
width: self.area.0,
|
||||
height: self.area.1,
|
||||
},
|
||||
})
|
||||
.layer_count(self.layers)
|
||||
.color_attachments(&color_attachments);
|
||||
let rendering_info = if let Some(depth) = depth_attachment.as_ref() {
|
||||
rendering_info.depth_attachment(depth)
|
||||
} else {
|
||||
rendering_info
|
||||
};
|
||||
let rendering_info = if let Some(stencil) = stencil_attachment.as_ref() {
|
||||
rendering_info.stencil_attachment(stencil)
|
||||
} else {
|
||||
rendering_info
|
||||
};
|
||||
unsafe {
|
||||
cmd.device()
|
||||
.raw
|
||||
.cmd_begin_rendering(cmd.raw(), &rendering_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EndRendering;
|
||||
|
|
@ -346,6 +468,31 @@ impl Command for EndRendering {
|
|||
// No resource access, but ends the render pass
|
||||
}
|
||||
|
||||
fn apply(self, recorder: &mut CommandRecorder) {
|
||||
let cmd = recorder.cmd_buffer();
|
||||
unsafe {
|
||||
cmd.device().raw.cmd_end_rendering(cmd.raw());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PreparePresent {
|
||||
pub texture: TextureRegion,
|
||||
}
|
||||
|
||||
impl Command for PreparePresent {
|
||||
fn side_effects(&self, mut map: SideEffectMap) {
|
||||
// The present layout differs from the preceding color-attachment
|
||||
// layout, so try_fold rejects this access and conflicts emits the
|
||||
// release barrier even though this access has no memory scope.
|
||||
self.texture
|
||||
.side_effect(map.reborrow().into_side_effect_map2(
|
||||
vk::PipelineStageFlags2::NONE,
|
||||
vk::AccessFlags2::NONE,
|
||||
Some(vk::ImageLayout::PRESENT_SRC_KHR),
|
||||
));
|
||||
}
|
||||
|
||||
fn apply(self, _recorder: &mut CommandRecorder) {}
|
||||
}
|
||||
|
||||
|
|
@ -475,7 +622,20 @@ impl Command for SetViewport {
|
|||
// No resource access, but affects the viewport state
|
||||
}
|
||||
|
||||
fn apply(self, _recorder: &mut CommandRecorder) {}
|
||||
fn apply(self, recorder: &mut CommandRecorder) {
|
||||
let cmd = recorder.cmd_buffer();
|
||||
let viewport = vk::Viewport {
|
||||
x: self.x,
|
||||
y: self.y,
|
||||
width: self.width,
|
||||
height: self.height,
|
||||
min_depth: self.min_depth,
|
||||
max_depth: self.max_depth,
|
||||
};
|
||||
unsafe {
|
||||
cmd.device().raw.cmd_set_viewport(cmd.raw(), 0, &[viewport]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SetScissor {
|
||||
|
|
@ -490,7 +650,22 @@ impl Command for SetScissor {
|
|||
// No resource access, but affects the scissor state
|
||||
}
|
||||
|
||||
fn apply(self, _recorder: &mut CommandRecorder) {}
|
||||
fn apply(self, recorder: &mut CommandRecorder) {
|
||||
let cmd = recorder.cmd_buffer();
|
||||
let scissor = vk::Rect2D {
|
||||
offset: vk::Offset2D {
|
||||
x: self.x as i32,
|
||||
y: self.y as i32,
|
||||
},
|
||||
extent: vk::Extent2D {
|
||||
width: self.width,
|
||||
height: self.height,
|
||||
},
|
||||
};
|
||||
unsafe {
|
||||
cmd.device().raw.cmd_set_scissor(cmd.raw(), 0, &[scissor]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PushConstants {
|
||||
|
|
@ -580,16 +755,34 @@ impl<T: IsDrawData> Command for Draw<T> {
|
|||
}
|
||||
}
|
||||
|
||||
fn apply(self, _recorder: &mut CommandRecorder) {}
|
||||
fn apply(self, recorder: &mut CommandRecorder) {
|
||||
self.data
|
||||
.apply(recorder, self.vertex_buffers, self.index_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PipelineBarrier;
|
||||
pub struct PipelineBarrier {
|
||||
pub image_barriers: Vec<vk::ImageMemoryBarrier2<'static>>,
|
||||
pub buffer_barriers: Vec<vk::BufferMemoryBarrier2<'static>>,
|
||||
pub(crate) image_layouts: Vec<(ResourceId, vk::ImageLayout)>,
|
||||
}
|
||||
|
||||
impl Command for PipelineBarrier {
|
||||
fn side_effects(&self, _map: SideEffectMap) {}
|
||||
|
||||
fn apply(self, _recorder: &mut CommandRecorder) {
|
||||
todo!()
|
||||
fn apply(self, recorder: &mut CommandRecorder) {
|
||||
for (resource, layout) in self.image_layouts {
|
||||
recorder.set_image_layout(resource, layout);
|
||||
}
|
||||
let cmd = recorder.cmd_buffer();
|
||||
let dependency_info = vk::DependencyInfo::default()
|
||||
.image_memory_barriers(&self.image_barriers)
|
||||
.buffer_memory_barriers(&self.buffer_barriers);
|
||||
unsafe {
|
||||
cmd.device()
|
||||
.raw
|
||||
.cmd_pipeline_barrier2(cmd.raw(), &dependency_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -599,8 +792,16 @@ pub trait Command: Sized {
|
|||
}
|
||||
|
||||
mod sealed {
|
||||
use super::*;
|
||||
|
||||
pub trait IsDrawData {
|
||||
fn side_effects(&self, _map: super::SideEffectMap) {}
|
||||
fn apply(
|
||||
self,
|
||||
recorder: &mut CommandRecorder,
|
||||
vertex_buffers: Vec<BufferSlice>,
|
||||
index_buffer: Option<(BufferSlice, IndexFormat)>,
|
||||
);
|
||||
}
|
||||
pub trait InsideRenderPass {}
|
||||
pub trait OutsideRenderPass {}
|
||||
|
|
@ -608,8 +809,47 @@ mod sealed {
|
|||
use ash::vk;
|
||||
pub(super) use sealed::*;
|
||||
|
||||
impl IsDrawData for DrawData {}
|
||||
impl IsDrawData for DrawIndexedData {}
|
||||
impl IsDrawData for DrawData {
|
||||
fn apply(
|
||||
self,
|
||||
recorder: &mut CommandRecorder,
|
||||
vertex_buffers: Vec<BufferSlice>,
|
||||
index_buffer: Option<(BufferSlice, IndexFormat)>,
|
||||
) {
|
||||
bind_draw_buffers(recorder, &vertex_buffers, index_buffer.as_ref());
|
||||
let cmd = recorder.cmd_buffer();
|
||||
unsafe {
|
||||
cmd.device().raw.cmd_draw(
|
||||
cmd.raw(),
|
||||
self.vertex_count,
|
||||
self.instance_count,
|
||||
self.first_vertex,
|
||||
self.first_instance,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
impl IsDrawData for DrawIndexedData {
|
||||
fn apply(
|
||||
self,
|
||||
recorder: &mut CommandRecorder,
|
||||
vertex_buffers: Vec<BufferSlice>,
|
||||
index_buffer: Option<(BufferSlice, IndexFormat)>,
|
||||
) {
|
||||
bind_draw_buffers(recorder, &vertex_buffers, index_buffer.as_ref());
|
||||
let cmd = recorder.cmd_buffer();
|
||||
unsafe {
|
||||
cmd.device().raw.cmd_draw_indexed(
|
||||
cmd.raw(),
|
||||
self.index_count,
|
||||
self.instance_count,
|
||||
self.first_index,
|
||||
self.vertex_offset,
|
||||
self.first_instance,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
impl IsDrawData for DrawIndirectData {
|
||||
fn side_effects(&self, mut map: SideEffectMap) {
|
||||
self.indirect_buffer
|
||||
|
|
@ -619,6 +859,24 @@ impl IsDrawData for DrawIndirectData {
|
|||
None,
|
||||
));
|
||||
}
|
||||
fn apply(
|
||||
self,
|
||||
recorder: &mut CommandRecorder,
|
||||
vertex_buffers: Vec<BufferSlice>,
|
||||
index_buffer: Option<(BufferSlice, IndexFormat)>,
|
||||
) {
|
||||
bind_draw_buffers(recorder, &vertex_buffers, index_buffer.as_ref());
|
||||
let cmd = recorder.cmd_buffer();
|
||||
unsafe {
|
||||
cmd.device().raw.cmd_draw_indirect(
|
||||
cmd.raw(),
|
||||
recorder.get_buffer_handle(self.indirect_buffer.id()),
|
||||
self.indirect_buffer.range.offset,
|
||||
self.count,
|
||||
self.stride,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
impl IsDrawData for DrawIndexedIndirectData {
|
||||
fn side_effects(&self, mut map: SideEffectMap) {
|
||||
|
|
@ -629,9 +887,65 @@ impl IsDrawData for DrawIndexedIndirectData {
|
|||
None,
|
||||
));
|
||||
}
|
||||
fn apply(
|
||||
self,
|
||||
recorder: &mut CommandRecorder,
|
||||
vertex_buffers: Vec<BufferSlice>,
|
||||
index_buffer: Option<(BufferSlice, IndexFormat)>,
|
||||
) {
|
||||
bind_draw_buffers(recorder, &vertex_buffers, index_buffer.as_ref());
|
||||
let cmd = recorder.cmd_buffer();
|
||||
unsafe {
|
||||
cmd.device().raw.cmd_draw_indexed_indirect(
|
||||
cmd.raw(),
|
||||
recorder.get_buffer_handle(self.indirect_buffer.id()),
|
||||
self.indirect_buffer.range.offset,
|
||||
self.count,
|
||||
self.stride,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn bind_draw_buffers(
|
||||
recorder: &mut CommandRecorder,
|
||||
vertex_buffers: &[BufferSlice],
|
||||
index_buffer: Option<&(BufferSlice, IndexFormat)>,
|
||||
) {
|
||||
let cmd = recorder.cmd_buffer();
|
||||
let handles = vertex_buffers
|
||||
.iter()
|
||||
.map(|buffer| recorder.get_buffer_handle(buffer.id()))
|
||||
.collect::<Vec<_>>();
|
||||
let offsets = vertex_buffers
|
||||
.iter()
|
||||
.map(|buffer| buffer.range.offset)
|
||||
.collect::<Vec<_>>();
|
||||
unsafe {
|
||||
if !handles.is_empty() {
|
||||
cmd.device()
|
||||
.raw
|
||||
.cmd_bind_vertex_buffers(cmd.raw(), 0, &handles, &offsets);
|
||||
}
|
||||
if let Some((buffer, format)) = index_buffer {
|
||||
let index_type = match format {
|
||||
IndexFormat::Uint8 => vk::IndexType::UINT8_KHR,
|
||||
IndexFormat::Uint16 => vk::IndexType::UINT16,
|
||||
IndexFormat::Uint32 => vk::IndexType::UINT32,
|
||||
};
|
||||
cmd.device().raw.cmd_bind_index_buffer(
|
||||
cmd.raw(),
|
||||
recorder.get_buffer_handle(buffer.id()),
|
||||
buffer.range.offset,
|
||||
index_type,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OutsideRenderPass for BeginRendering {}
|
||||
impl<T: Resource> OutsideRenderPass for ImportResource<T> {}
|
||||
impl OutsideRenderPass for PreparePresent {}
|
||||
impl InsideRenderPass for EndRendering {}
|
||||
impl<T: Resource, U: Resource> OutsideRenderPass for Copy<T, U> {}
|
||||
impl OutsideRenderPass for ClearTexture {}
|
||||
|
|
|
|||
|
|
@ -1,62 +1,232 @@
|
|||
use std::{cell::UnsafeCell, collections::BTreeMap, marker::PhantomData};
|
||||
use std::{
|
||||
cell::Cell,
|
||||
collections::BTreeMap,
|
||||
marker::PhantomData,
|
||||
ops::{Deref, DerefMut},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use ash::vk;
|
||||
use derive_more::derive::{Deref, DerefMut};
|
||||
use gpu_allocator::MemoryLocation;
|
||||
use parking_lot::RwLock;
|
||||
|
||||
use crate::{
|
||||
buffers, images,
|
||||
Device, buffers,
|
||||
device::{AllocationStrategy, QueueFlags},
|
||||
images,
|
||||
render_graph::{
|
||||
recorder::CommandList,
|
||||
resources::{self, ResourceId},
|
||||
},
|
||||
};
|
||||
|
||||
pub struct GraphResourceRegistry {
|
||||
images: BTreeMap<ResourceId, UnsafeCell<images::Image>>,
|
||||
views: BTreeMap<ResourceId, UnsafeCell<images::ImageView>>,
|
||||
buffers: BTreeMap<ResourceId, UnsafeCell<buffers::Buffer>>,
|
||||
#[derive(Default)]
|
||||
pub(crate) struct GraphResourceRegistry {
|
||||
pub(crate) images: RwLock<BTreeMap<ResourceId, Arc<images::Image>>>,
|
||||
pub(crate) views: RwLock<BTreeMap<ResourceId, images::ImageView>>,
|
||||
pub(crate) buffers: RwLock<BTreeMap<ResourceId, Arc<std::cell::UnsafeCell<buffers::Buffer>>>>,
|
||||
pub(crate) shared_buffers: RwLock<BTreeMap<ResourceId, Arc<buffers::Buffer>>>,
|
||||
pub(crate) owning_queues: RwLock<BTreeMap<ResourceId, Option<u32>>>,
|
||||
}
|
||||
|
||||
#[derive(Deref, DerefMut)]
|
||||
struct StagingBuffer<'a> {
|
||||
id: ResourceId,
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
buffer: &'a mut buffers::Buffer,
|
||||
pub struct StagingBuffer<'a> {
|
||||
pub id: ResourceId,
|
||||
buffer: Arc<std::cell::UnsafeCell<buffers::Buffer>>,
|
||||
_pd: PhantomData<&'a mut buffers::Buffer>,
|
||||
}
|
||||
|
||||
#[derive(Deref, DerefMut)]
|
||||
struct Buffer<'a> {
|
||||
id: ResourceId,
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
buffer: &'a mut buffers::Buffer,
|
||||
pub struct Buffer<'a> {
|
||||
pub id: ResourceId,
|
||||
buffer: Arc<std::cell::UnsafeCell<buffers::Buffer>>,
|
||||
_pd: PhantomData<&'a mut buffers::Buffer>,
|
||||
}
|
||||
|
||||
impl Deref for StagingBuffer<'_> {
|
||||
type Target = buffers::Buffer;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
// The wrapper is unique and not Clone; the frame driver must not start
|
||||
// recording from the registry until all wrappers have been dropped.
|
||||
unsafe { &*self.buffer.get() }
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for StagingBuffer<'_> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
// The wrapper is unique and not Clone; the frame driver must not start
|
||||
// recording from the registry until all wrappers have been dropped.
|
||||
unsafe { &mut *self.buffer.get() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Buffer<'_> {
|
||||
type Target = buffers::Buffer;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
// See the StagingBuffer deref safety invariant above.
|
||||
unsafe { &*self.buffer.get() }
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Buffer<'_> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
// See the StagingBuffer deref safety invariant above.
|
||||
unsafe { &mut *self.buffer.get() }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GraphBuilder<'r> {
|
||||
_pd: PhantomData<fn(&'r ())>,
|
||||
device: Device,
|
||||
registry: Arc<GraphResourceRegistry>,
|
||||
next_id: Cell<u64>,
|
||||
_pd: PhantomData<fn(&'r mut ())>,
|
||||
}
|
||||
|
||||
enum GraphResource {
|
||||
Buffer(buffers::Buffer, Option<u32>),
|
||||
SharedBuffer(Arc<buffers::Buffer>, Option<u32>),
|
||||
Image(Arc<images::Image>),
|
||||
}
|
||||
|
||||
impl<'r> GraphBuilder<'r> {
|
||||
fn make_command_list<'cmd>(&self) -> CommandList<'cmd> {
|
||||
todo!()
|
||||
pub fn new(device: Device) -> Self {
|
||||
Self {
|
||||
device,
|
||||
registry: Arc::new(GraphResourceRegistry::default()),
|
||||
next_id: Cell::new(0),
|
||||
_pd: PhantomData,
|
||||
}
|
||||
fn make_staging_buffer(&self, _size: u64) -> StagingBuffer<'r> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn make_buffer(
|
||||
pub(crate) fn registry(&self) -> Arc<GraphResourceRegistry> {
|
||||
Arc::clone(&self.registry)
|
||||
}
|
||||
|
||||
fn register_resource(&self, resource: GraphResource) -> ResourceId {
|
||||
let id = ResourceId(self.next_id.get());
|
||||
self.next_id.set(
|
||||
self.next_id
|
||||
.get()
|
||||
.checked_add(1)
|
||||
.expect("graph resource ID counter overflowed"),
|
||||
);
|
||||
|
||||
match resource {
|
||||
GraphResource::Buffer(buffer, owning_queue) => {
|
||||
self.registry
|
||||
.buffers
|
||||
.write()
|
||||
.insert(id, Arc::new(std::cell::UnsafeCell::new(buffer)));
|
||||
self.registry.owning_queues.write().insert(id, owning_queue);
|
||||
}
|
||||
GraphResource::SharedBuffer(buffer, owning_queue) => {
|
||||
self.registry.shared_buffers.write().insert(id, buffer);
|
||||
self.registry.owning_queues.write().insert(id, owning_queue);
|
||||
}
|
||||
GraphResource::Image(image) => {
|
||||
self.registry.images.write().insert(id, image);
|
||||
}
|
||||
}
|
||||
id
|
||||
}
|
||||
|
||||
fn buffer_wrapper(&self, id: ResourceId) -> Buffer<'r> {
|
||||
let buffer = self
|
||||
.registry
|
||||
.buffers
|
||||
.read()
|
||||
.get(&id)
|
||||
.expect("new graph buffer was not registered")
|
||||
.clone();
|
||||
Buffer {
|
||||
id,
|
||||
buffer,
|
||||
_pd: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
fn staging_buffer_wrapper(&self, id: ResourceId) -> StagingBuffer<'r> {
|
||||
let buffer = self
|
||||
.registry
|
||||
.buffers
|
||||
.read()
|
||||
.get(&id)
|
||||
.expect("new graph staging buffer was not registered")
|
||||
.clone();
|
||||
StagingBuffer {
|
||||
id,
|
||||
buffer,
|
||||
_pd: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_command_list<'cmd>(&self) -> CommandList<'cmd> {
|
||||
CommandList::new()
|
||||
}
|
||||
|
||||
pub fn make_staging_buffer(&self, size: u64) -> StagingBuffer<'r> {
|
||||
let buffer = buffers::Buffer::new(
|
||||
self.device.clone(),
|
||||
buffers::BufferDesc {
|
||||
size,
|
||||
usage: vk::BufferUsageFlags::TRANSFER_SRC,
|
||||
mem_location: MemoryLocation::CpuToGpu,
|
||||
queue_families: QueueFlags::empty(),
|
||||
alloc_scheme: AllocationStrategy::AllocatorManaged,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.expect("failed to create graph staging buffer");
|
||||
let id = self.register_resource(GraphResource::Buffer(buffer, None));
|
||||
self.staging_buffer_wrapper(id)
|
||||
}
|
||||
|
||||
pub fn make_buffer(
|
||||
&self,
|
||||
_size: u64,
|
||||
_usage: vk::BufferUsageFlags,
|
||||
_location: gpu_allocator::MemoryLocation,
|
||||
size: u64,
|
||||
usage: vk::BufferUsageFlags,
|
||||
location: MemoryLocation,
|
||||
) -> Buffer<'r> {
|
||||
todo!()
|
||||
let buffer = buffers::Buffer::new(
|
||||
self.device.clone(),
|
||||
buffers::BufferDesc {
|
||||
size,
|
||||
usage,
|
||||
mem_location: location,
|
||||
queue_families: QueueFlags::empty(),
|
||||
alloc_scheme: AllocationStrategy::AllocatorManaged,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.expect("failed to create graph buffer");
|
||||
let id = self.register_resource(GraphResource::Buffer(buffer, None));
|
||||
self.buffer_wrapper(id)
|
||||
}
|
||||
|
||||
// we need to know which queue currently owns this buffer to be able to
|
||||
// transfer it to the correct queue family when using it
|
||||
fn import_buffer(&self, _buffer: buffers::Buffer, _owning_queue: Option<u32>) -> Buffer<'r> {
|
||||
todo!()
|
||||
pub fn import_buffer(&self, buffer: buffers::Buffer, owning_queue: Option<u32>) -> Buffer<'r> {
|
||||
let id = self.register_resource(GraphResource::Buffer(buffer, owning_queue));
|
||||
self.buffer_wrapper(id)
|
||||
}
|
||||
|
||||
pub fn import_shared_buffer(
|
||||
&self,
|
||||
buffer: Arc<buffers::Buffer>,
|
||||
owning_queue: Option<u32>,
|
||||
) -> ResourceId {
|
||||
self.register_resource(GraphResource::SharedBuffer(buffer, owning_queue))
|
||||
}
|
||||
|
||||
pub fn make_image(&self, desc: images::ImageDesc) -> resources::Texture {
|
||||
let image = Arc::new(
|
||||
images::Image::new(self.device.clone(), desc).expect("failed to create graph image"),
|
||||
);
|
||||
resources::Texture(self.register_resource(GraphResource::Image(image)))
|
||||
}
|
||||
|
||||
pub fn import_image(&self, image: Arc<images::Image>) -> resources::Texture {
|
||||
resources::Texture(self.register_resource(GraphResource::Image(image)))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -231,7 +401,8 @@ mod api_demo {
|
|||
});
|
||||
|
||||
let mut pass_cmd = cmd.begin_rendering(
|
||||
vec![resources::TextureRegion {
|
||||
vec![commands::RenderingAttachment {
|
||||
region: resources::TextureRegion {
|
||||
texture: resources::Texture(render_target),
|
||||
range: resources::TextureRange {
|
||||
aspect: vk::ImageAspectFlags::COLOR,
|
||||
|
|
@ -240,6 +411,12 @@ mod api_demo {
|
|||
origin: (0, 0, 0),
|
||||
extent: (extent.0, extent.1, 1),
|
||||
},
|
||||
},
|
||||
load_op: vk::AttachmentLoadOp::CLEAR,
|
||||
store_op: vk::AttachmentStoreOp::STORE,
|
||||
clear_value: vk::ClearValue {
|
||||
color: vk::ClearColorValue { float32: [0.0; 4] },
|
||||
},
|
||||
}],
|
||||
None,
|
||||
None,
|
||||
|
|
@ -262,9 +439,6 @@ mod api_demo {
|
|||
max_depth: 1.0,
|
||||
});
|
||||
|
||||
fn factory<T>() -> T {
|
||||
unimplemented!()
|
||||
}
|
||||
pass_cmd.push(commands::BindPipeline(
|
||||
pipeline.raw(),
|
||||
pipeline.bind_point(),
|
||||
|
|
|
|||
|
|
@ -7,3 +7,15 @@ mod commands;
|
|||
mod graph_builder;
|
||||
mod recorder;
|
||||
mod resources;
|
||||
|
||||
pub use commands::{
|
||||
BindPipeline, Draw, DrawIndexedData, ImportResource, IndexFormat, PreparePresent,
|
||||
PushConstants, RenderingAttachment, SetScissor, SetViewport,
|
||||
};
|
||||
pub use graph_builder::GraphBuilder;
|
||||
pub use recorder::CommandList;
|
||||
pub(crate) use recorder::CommandRecorder;
|
||||
pub use resources::{
|
||||
Buffer as GraphBuffer, BufferRange, BufferSlice, ResourceId, Texture, TextureRange,
|
||||
TextureRegion,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,44 +3,131 @@ use std::{
|
|||
marker::PhantomData,
|
||||
mem::{MaybeUninit, size_of},
|
||||
ptr::NonNull,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use ash::vk;
|
||||
use ash_formats::FormatExt;
|
||||
|
||||
use crate::{
|
||||
commands::CommandBuffer,
|
||||
images::ImageDesc,
|
||||
images::{ImageDesc, ImageViewDesc},
|
||||
render_graph::{
|
||||
commands::{Command, InsideRenderPass, OutsideRenderPass},
|
||||
resources::{ResourceAccess, ResourceId, ResourceRange, TextureRegion},
|
||||
resources::{ResourceAccess, ResourceId, ResourceRange},
|
||||
},
|
||||
};
|
||||
|
||||
pub struct CommandRecorder;
|
||||
use super::graph_builder::GraphResourceRegistry;
|
||||
|
||||
pub struct CommandRecorder {
|
||||
cmd_buffer: CommandBuffer,
|
||||
registry: Arc<GraphResourceRegistry>,
|
||||
layouts: BTreeMap<ResourceId, vk::ImageLayout>,
|
||||
}
|
||||
|
||||
impl CommandRecorder {
|
||||
pub(crate) fn new(cmd_buffer: CommandBuffer, registry: Arc<GraphResourceRegistry>) -> Self {
|
||||
Self {
|
||||
cmd_buffer,
|
||||
registry,
|
||||
layouts: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cmd_buffer(&self) -> &CommandBuffer {
|
||||
unimplemented!()
|
||||
&self.cmd_buffer
|
||||
}
|
||||
|
||||
pub fn get_buffer_handle(&self, _id: ResourceId) -> vk::Buffer {
|
||||
unimplemented!()
|
||||
pub fn into_command_buffer(self) -> CommandBuffer {
|
||||
self.cmd_buffer
|
||||
}
|
||||
|
||||
pub fn get_image_desc(&self, _id: ResourceId) -> &ImageDesc {
|
||||
unimplemented!()
|
||||
pub fn get_buffer_handle(&self, id: ResourceId) -> vk::Buffer {
|
||||
if let Some(buffer) = self.registry.buffers.read().get(&id) {
|
||||
unsafe { (&*buffer.get()).raw() }
|
||||
} else {
|
||||
self.registry
|
||||
.shared_buffers
|
||||
.read()
|
||||
.get(&id)
|
||||
.expect("unknown graph buffer")
|
||||
.raw()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_image_handle(&self, _id: ResourceId) -> vk::Image {
|
||||
unimplemented!()
|
||||
pub fn get_image_desc(&self, id: ResourceId) -> ImageDesc {
|
||||
self.registry
|
||||
.images
|
||||
.read()
|
||||
.get(&id)
|
||||
.expect("unknown graph image")
|
||||
.desc()
|
||||
.clone()
|
||||
}
|
||||
|
||||
pub fn get_image_layout(&self, _id: ResourceId) -> vk::ImageLayout {
|
||||
unimplemented!()
|
||||
pub fn get_image_handle(&self, id: ResourceId) -> vk::Image {
|
||||
self.registry
|
||||
.images
|
||||
.read()
|
||||
.get(&id)
|
||||
.expect("unknown graph image")
|
||||
.raw()
|
||||
}
|
||||
|
||||
pub fn get_image_view_handle(&self, _id: ResourceId) -> vk::ImageView {
|
||||
unimplemented!()
|
||||
pub fn get_image_layout(&self, id: ResourceId) -> vk::ImageLayout {
|
||||
self.layouts
|
||||
.get(&id)
|
||||
.copied()
|
||||
.unwrap_or(vk::ImageLayout::UNDEFINED)
|
||||
}
|
||||
|
||||
pub fn get_image_view_handle(&self, id: ResourceId) -> vk::ImageView {
|
||||
if let Some(view) = self.registry.views.read().get(&id) {
|
||||
return view.raw();
|
||||
}
|
||||
|
||||
let mut views = self.registry.views.write();
|
||||
if let Some(view) = views.get(&id) {
|
||||
return view.raw();
|
||||
}
|
||||
|
||||
let image = self
|
||||
.registry
|
||||
.images
|
||||
.read()
|
||||
.get(&id)
|
||||
.expect("unknown graph image")
|
||||
.clone();
|
||||
let desc = image.desc();
|
||||
let view_desc = ImageViewDesc {
|
||||
name: None,
|
||||
flags: vk::ImageViewCreateFlags::empty(),
|
||||
kind: match desc.kind {
|
||||
vk::ImageType::TYPE_1D if desc.array_layers > 1 => vk::ImageViewType::TYPE_1D_ARRAY,
|
||||
vk::ImageType::TYPE_1D => vk::ImageViewType::TYPE_1D,
|
||||
vk::ImageType::TYPE_2D if desc.array_layers > 1 => vk::ImageViewType::TYPE_2D_ARRAY,
|
||||
vk::ImageType::TYPE_2D => vk::ImageViewType::TYPE_2D,
|
||||
vk::ImageType::TYPE_3D => vk::ImageViewType::TYPE_3D,
|
||||
_ => unreachable!("unsupported image type"),
|
||||
},
|
||||
format: desc.format,
|
||||
aspect: desc.format.aspect_flags(),
|
||||
mip_range: desc.mip_level_range(),
|
||||
layer_range: desc.layer_range(),
|
||||
components: vk::ComponentMapping::default(),
|
||||
};
|
||||
let view = image
|
||||
.create_view(view_desc)
|
||||
.expect("failed to create a full-range graph image view");
|
||||
let view = std::mem::ManuallyDrop::into_inner(view);
|
||||
let handle = view.raw();
|
||||
views.insert(id, view);
|
||||
handle
|
||||
}
|
||||
|
||||
pub(crate) fn set_image_layout(&mut self, id: ResourceId, layout: vk::ImageLayout) {
|
||||
self.layouts.insert(id, layout);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -186,9 +273,9 @@ impl<'cmd> CommandList<'cmd> {
|
|||
|
||||
pub fn begin_rendering(
|
||||
&mut self,
|
||||
color_attachments: Vec<TextureRegion>,
|
||||
depth_attachment: Option<TextureRegion>,
|
||||
stencil_attachment: Option<TextureRegion>,
|
||||
color_attachments: Vec<super::commands::RenderingAttachment>,
|
||||
depth_attachment: Option<super::commands::RenderingAttachment>,
|
||||
stencil_attachment: Option<super::commands::RenderingAttachment>,
|
||||
area: (u32, u32),
|
||||
layers: u32,
|
||||
) -> RenderPass<'cmd, '_> {
|
||||
|
|
@ -219,7 +306,7 @@ impl<'cmd> CommandList<'cmd> {
|
|||
let packed = Packed {
|
||||
meta: CommandMeta {
|
||||
apply_command: |recorder, command_ptr, cursor| {
|
||||
*cursor += size_of::<C>();
|
||||
*cursor += size_of::<CommandMeta>() + size_of::<C>();
|
||||
let command = unsafe { command_ptr.cast::<C>().read_unaligned() };
|
||||
C::apply(command, recorder);
|
||||
},
|
||||
|
|
@ -232,10 +319,14 @@ impl<'cmd> CommandList<'cmd> {
|
|||
unsafe {
|
||||
let bytes = self.command_bytes.as_mut_ptr().add(len);
|
||||
bytes.cast::<Packed<C>>().write_unaligned(packed);
|
||||
self.command_bytes.set_len(len + size_of::<Packed<C>>());
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_side_effects(&self) -> Vec<(u32, super::commands::PipelineBarrier)> {
|
||||
fn fold_side_effects(
|
||||
&self,
|
||||
recorder: &mut CommandRecorder,
|
||||
) -> Vec<(u32, super::commands::PipelineBarrier)> {
|
||||
// Buffer accesses can be granularly tracked by their offset and size;
|
||||
// Image accesses are tracked by their subresource range, which means all regions of the same mip/array layer are considered the same subresource for the purposes of ImageMemoryBarriers
|
||||
|
||||
|
|
@ -259,7 +350,7 @@ impl<'cmd> CommandList<'cmd> {
|
|||
resource_id: ResourceId,
|
||||
command_index: u32,
|
||||
from: ResourceAccess,
|
||||
to: usize,
|
||||
to: ResourceAccess,
|
||||
}
|
||||
|
||||
// map to keep track of the current state of each subresource
|
||||
|
|
@ -291,7 +382,6 @@ impl<'cmd> CommandList<'cmd> {
|
|||
// a barrier before the current command index.
|
||||
|
||||
let mut folded = false;
|
||||
let len = entry.len();
|
||||
for (_i, (sub, _state)) in &mut entry.iter_mut().enumerate().rev() {
|
||||
if !folded && sub.try_fold(&access) {
|
||||
access = *sub;
|
||||
|
|
@ -306,13 +396,11 @@ impl<'cmd> CommandList<'cmd> {
|
|||
// conflicting access must be of opposite polarity (or a
|
||||
// layout transition) and no more accesses can be folded
|
||||
// into it.
|
||||
// we store the index of `to`, as more accesses may be
|
||||
// folded into it, expanding mips/array layers.
|
||||
barriers.push(Barrier {
|
||||
resource_id: *id,
|
||||
command_index: *command_index,
|
||||
from: *sub,
|
||||
to: len,
|
||||
to: access,
|
||||
});
|
||||
|
||||
// we can stop iterating over previous accesses, as we
|
||||
|
|
@ -335,23 +423,83 @@ impl<'cmd> CommandList<'cmd> {
|
|||
}
|
||||
}
|
||||
|
||||
// construct barriers
|
||||
// TODO: need to know each images aspects at this point
|
||||
for _barrier in barriers {
|
||||
// stuff
|
||||
let mut result = Vec::with_capacity(barriers.len());
|
||||
for barrier in barriers {
|
||||
let pipeline_barrier = match (&barrier.from.range, &barrier.to.range) {
|
||||
(
|
||||
ResourceRange::Texture {
|
||||
layout: old_layout, ..
|
||||
},
|
||||
ResourceRange::Texture {
|
||||
aspect,
|
||||
mip_level,
|
||||
array_layers,
|
||||
layout: new_layout,
|
||||
..
|
||||
},
|
||||
) => {
|
||||
let format_aspects = recorder
|
||||
.get_image_desc(barrier.resource_id)
|
||||
.format
|
||||
.aspect_flags();
|
||||
let aspects = *aspect & format_aspects;
|
||||
if aspects.is_empty() {
|
||||
tracing::debug!(resource = ?barrier.resource_id, "skipping image barrier with no compatible aspects");
|
||||
continue;
|
||||
}
|
||||
let image_barrier = vk::ImageMemoryBarrier2::default()
|
||||
.src_stage_mask(barrier.from.stages)
|
||||
.src_access_mask(barrier.from.access)
|
||||
.dst_stage_mask(barrier.to.stages)
|
||||
.dst_access_mask(barrier.to.access)
|
||||
.old_layout(*old_layout)
|
||||
.new_layout(*new_layout)
|
||||
.image(recorder.get_image_handle(barrier.resource_id))
|
||||
.subresource_range(
|
||||
vk::ImageSubresourceRange::default()
|
||||
.aspect_mask(aspects)
|
||||
.base_mip_level(mip_level.start())
|
||||
.level_count(mip_level.len())
|
||||
.base_array_layer(array_layers.start())
|
||||
.layer_count(array_layers.len()),
|
||||
);
|
||||
super::commands::PipelineBarrier {
|
||||
image_barriers: vec![image_barrier],
|
||||
buffer_barriers: Vec::new(),
|
||||
image_layouts: vec![(barrier.resource_id, *new_layout)],
|
||||
}
|
||||
}
|
||||
(ResourceRange::Buffer { offset, size }, ResourceRange::Buffer { .. }) => {
|
||||
let buffer_barrier = vk::BufferMemoryBarrier2::default()
|
||||
.src_stage_mask(barrier.from.stages)
|
||||
.src_access_mask(barrier.from.access)
|
||||
.dst_stage_mask(barrier.to.stages)
|
||||
.dst_access_mask(barrier.to.access)
|
||||
.buffer(recorder.get_buffer_handle(barrier.resource_id))
|
||||
.offset(*offset)
|
||||
.size(*size);
|
||||
super::commands::PipelineBarrier {
|
||||
image_barriers: Vec::new(),
|
||||
buffer_barriers: vec![buffer_barrier],
|
||||
image_layouts: Vec::new(),
|
||||
}
|
||||
}
|
||||
_ => continue,
|
||||
};
|
||||
result.push((barrier.command_index, pipeline_barrier));
|
||||
}
|
||||
result.sort_by_key(|(command_index, _)| *command_index);
|
||||
result
|
||||
}
|
||||
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn record_commands(&self, recorder: &mut CommandRecorder) {
|
||||
pub(crate) fn record_commands(&self, recorder: &mut CommandRecorder) {
|
||||
let mut cursor = 0;
|
||||
let mut barriers = self.fold_side_effects();
|
||||
let mut barriers = self.fold_side_effects(recorder);
|
||||
let mut command_index = 0;
|
||||
|
||||
while cursor < self.command_bytes.len() {
|
||||
barriers
|
||||
.drain(barriers.partition_point(|(idx, _)| *idx <= command_index)..)
|
||||
.drain(..barriers.partition_point(|(idx, _)| *idx <= command_index))
|
||||
.for_each(|(_, barrier)| {
|
||||
barrier.apply(recorder);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -17,7 +17,10 @@ use crate::{
|
|||
images::ImageViewDesc,
|
||||
pipeline,
|
||||
render_graph::{
|
||||
Access, GraphResourceId, PassDesc, RecordFn, RenderContext, RenderGraph, buffer_barrier,
|
||||
Access, BindPipeline, BufferRange, BufferSlice, Draw, DrawIndexedData, GraphBuffer,
|
||||
GraphBuilder, GraphResourceId, ImportResource, IndexFormat, PassDesc, PushConstants,
|
||||
RecordFn, RenderContext, RenderGraph, RenderingAttachment, SetScissor, SetViewport,
|
||||
Texture, TextureRange, TextureRegion, buffer_barrier,
|
||||
},
|
||||
sync,
|
||||
util::Rgba8,
|
||||
|
|
@ -164,14 +167,14 @@ impl Wireframe {
|
|||
);
|
||||
}
|
||||
|
||||
let pipeline = Self::create_pipeline(dev.clone())?;
|
||||
|
||||
let future = cmd.submit_async(
|
||||
None,
|
||||
None,
|
||||
Arc::new(sync::Fence::from_pool(&dev.pools.fences, None)?),
|
||||
)?;
|
||||
|
||||
let pipeline = Self::create_pipeline(dev.clone())?;
|
||||
|
||||
future.await;
|
||||
|
||||
Ok(Self {
|
||||
|
|
@ -212,13 +215,13 @@ impl Wireframe {
|
|||
flags: vk::PipelineShaderStageCreateFlags::empty(),
|
||||
module: &shader,
|
||||
stage: vk::ShaderStageFlags::FRAGMENT,
|
||||
entry: c"main".into(),
|
||||
entry: c"fragment".into(),
|
||||
},
|
||||
pipeline::ShaderStageDesc {
|
||||
flags: vk::PipelineShaderStageCreateFlags::empty(),
|
||||
module: &shader,
|
||||
stage: vk::ShaderStageFlags::VERTEX,
|
||||
entry: c"main".into(),
|
||||
entry: c"vertex".into(),
|
||||
},
|
||||
],
|
||||
render_pass: None,
|
||||
|
|
@ -380,4 +383,136 @@ impl Wireframe {
|
|||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn typed_pass(
|
||||
&self,
|
||||
builder: &mut GraphBuilder<'_>,
|
||||
target: Texture,
|
||||
extent: (u32, u32),
|
||||
list: &mut crate::render_graph::CommandList<'_>,
|
||||
) {
|
||||
// Buffers are shared with this long-lived scene object, so the typed
|
||||
// graph retains Arc references rather than taking ownership of them.
|
||||
let positions_size = self.positions.len();
|
||||
let indices_size = self.indices.len();
|
||||
let colors_size = self.colors.len();
|
||||
let positions = builder.import_shared_buffer(self.positions.clone(), None);
|
||||
let indices = builder.import_shared_buffer(self.indices.clone(), None);
|
||||
let colors = builder.import_shared_buffer(self.colors.clone(), None);
|
||||
|
||||
list.push(ImportResource {
|
||||
resource: GraphBuffer(positions),
|
||||
access: vk::AccessFlags2::VERTEX_ATTRIBUTE_READ,
|
||||
stage: vk::PipelineStageFlags2::VERTEX_ATTRIBUTE_INPUT,
|
||||
layout: None,
|
||||
});
|
||||
list.push(ImportResource {
|
||||
resource: GraphBuffer(indices),
|
||||
access: vk::AccessFlags2::INDEX_READ,
|
||||
stage: vk::PipelineStageFlags2::INDEX_INPUT,
|
||||
layout: None,
|
||||
});
|
||||
list.push(ImportResource {
|
||||
resource: GraphBuffer(colors),
|
||||
access: vk::AccessFlags2::VERTEX_ATTRIBUTE_READ,
|
||||
stage: vk::PipelineStageFlags2::VERTEX_ATTRIBUTE_INPUT,
|
||||
layout: None,
|
||||
});
|
||||
|
||||
let proj = proj::vulkan::perspective(
|
||||
core::f32::consts::FRAC_PI_4,
|
||||
extent.0 as f32 / extent.1 as f32,
|
||||
0.1,
|
||||
100.0,
|
||||
);
|
||||
let pos = vec3(0.0, 0.0, -5.0);
|
||||
let view = view::look_at_mat4(pos, pos + vec3(0.0, 0.0, 1.0), vec3(0.0, 1.0, 0.0));
|
||||
let to_clip = proj * view;
|
||||
|
||||
let mut pass = list.begin_rendering(
|
||||
vec![RenderingAttachment {
|
||||
region: TextureRegion {
|
||||
texture: target,
|
||||
range: TextureRange {
|
||||
aspect: vk::ImageAspectFlags::COLOR,
|
||||
mip_levels: (0..1).into(),
|
||||
array_layers: (0..1).into(),
|
||||
origin: (0, 0, 0),
|
||||
extent: (extent.0, extent.1, 1),
|
||||
},
|
||||
},
|
||||
load_op: vk::AttachmentLoadOp::CLEAR,
|
||||
store_op: vk::AttachmentStoreOp::STORE,
|
||||
clear_value: vk::ClearValue {
|
||||
color: vk::ClearColorValue {
|
||||
float32: [0.1, 0.1, 0.12, 1.0],
|
||||
},
|
||||
},
|
||||
}],
|
||||
None,
|
||||
None,
|
||||
extent,
|
||||
1,
|
||||
);
|
||||
pass.push(SetScissor {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: extent.0,
|
||||
height: extent.1,
|
||||
});
|
||||
pass.push(SetViewport {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
width: extent.0 as f32,
|
||||
height: extent.1 as f32,
|
||||
min_depth: 0.0,
|
||||
max_depth: 1.0,
|
||||
});
|
||||
pass.push(BindPipeline(
|
||||
self.pipeline.raw(),
|
||||
vk::PipelineBindPoint::GRAPHICS,
|
||||
));
|
||||
pass.push(PushConstants {
|
||||
pipeline_layout: self.pipeline.layout().raw(),
|
||||
stage_flags: vk::ShaderStageFlags::VERTEX,
|
||||
offset: 0,
|
||||
data: bytemuck::cast_slice(&[to_clip]).to_vec(),
|
||||
});
|
||||
pass.push(Draw {
|
||||
data: DrawIndexedData {
|
||||
index_count: self.num_indices,
|
||||
instance_count: 1,
|
||||
first_index: 0,
|
||||
vertex_offset: 0,
|
||||
first_instance: 0,
|
||||
},
|
||||
vertex_buffers: vec![
|
||||
BufferSlice {
|
||||
buffer: GraphBuffer(positions),
|
||||
range: BufferRange {
|
||||
offset: 0,
|
||||
size: positions_size,
|
||||
},
|
||||
},
|
||||
BufferSlice {
|
||||
buffer: GraphBuffer(colors),
|
||||
range: BufferRange {
|
||||
offset: 0,
|
||||
size: colors_size,
|
||||
},
|
||||
},
|
||||
],
|
||||
index_buffer: Some((
|
||||
BufferSlice {
|
||||
buffer: GraphBuffer(indices),
|
||||
range: BufferRange {
|
||||
offset: 0,
|
||||
size: indices_size,
|
||||
},
|
||||
},
|
||||
IndexFormat::Uint32,
|
||||
)),
|
||||
});
|
||||
pass.finalise();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue