Luna: add command impls, simple wireframe demo
This commit is contained in:
parent
cb02051cf3
commit
ab0812ff72
|
|
@ -31,6 +31,8 @@ struct WinitState {
|
||||||
renderer: Renderer2,
|
renderer: Renderer2,
|
||||||
last_redraw: std::time::Instant,
|
last_redraw: std::time::Instant,
|
||||||
egui_state: renderer::EguiState,
|
egui_state: renderer::EguiState,
|
||||||
|
wireframe: Option<renderer::rendering::Wireframe>,
|
||||||
|
typed_graph: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WinitState {
|
impl WinitState {
|
||||||
|
|
@ -38,6 +40,29 @@ impl WinitState {
|
||||||
const BASE_HEIGHT: u32 = 600;
|
const BASE_HEIGHT: u32 = 600;
|
||||||
fn new(window_title: String, display: DisplayHandle) -> WinitState {
|
fn new(window_title: String, display: DisplayHandle) -> WinitState {
|
||||||
let renderer = Renderer2::new(display.as_raw()).expect("renderer");
|
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 {
|
Self {
|
||||||
windows: BTreeMap::new(),
|
windows: BTreeMap::new(),
|
||||||
|
|
@ -49,6 +74,8 @@ impl WinitState {
|
||||||
// TODO: pass down this error and add some kind of error handling UI or dump
|
// TODO: pass down this error and add some kind of error handling UI or dump
|
||||||
egui_state: renderer::EguiState::new(renderer.device().clone()).unwrap(),
|
egui_state: renderer::EguiState::new(renderer.device().clone()).unwrap(),
|
||||||
renderer,
|
renderer,
|
||||||
|
wireframe,
|
||||||
|
typed_graph,
|
||||||
last_redraw: std::time::Instant::now(),
|
last_redraw: std::time::Instant::now(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -99,6 +126,20 @@ impl WinitState {
|
||||||
.filter_map(|id| self.egui_state.lookup_texture(*id))
|
.filter_map(|id| self.egui_state.lookup_texture(*id))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
// rendering
|
// 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| {
|
let render = self.renderer.draw_graph(&window.surface, |renderer, rg| {
|
||||||
use renderer::rendering::{egui_pass, egui_pre_pass};
|
use renderer::rendering::{egui_pass, egui_pre_pass};
|
||||||
let framebuffer = rg.get_framebuffer().unwrap();
|
let framebuffer = rg.get_framebuffer().unwrap();
|
||||||
|
|
@ -130,8 +171,10 @@ impl WinitState {
|
||||||
|
|
||||||
renderer::Result::Ok(())
|
renderer::Result::Ok(())
|
||||||
});
|
});
|
||||||
|
smol::block_on(render).flatten()
|
||||||
|
};
|
||||||
|
|
||||||
match smol::block_on(render).flatten() {
|
match render_result {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!("encountered error while rendering: {e}");
|
tracing::error!("encountered error while rendering: {e}");
|
||||||
|
|
|
||||||
|
|
@ -335,7 +335,13 @@ pub mod traits {
|
||||||
|
|
||||||
tracing::trace!("submitted queue {:?} and fence {:?}", queue, fence);
|
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(
|
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 {
|
mod command_pools {
|
||||||
use std::{borrow::Cow, cell::Cell, mem::ManuallyDrop, sync::Arc};
|
use std::{borrow::Cow, cell::Cell, mem::ManuallyDrop, sync::Arc};
|
||||||
|
|
@ -689,11 +697,13 @@ mod command_pools {
|
||||||
|
|
||||||
pub fn free_command_buffers(&self) {
|
pub fn free_command_buffers(&self) {
|
||||||
self.free_list_mut(|levels, buffers| {
|
self.free_list_mut(|levels, buffers| {
|
||||||
|
if !buffers.is_empty() {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.device()
|
self.device()
|
||||||
.dev()
|
.dev()
|
||||||
.free_command_buffers(self.handle(), &buffers);
|
.free_command_buffers(self.handle(), &buffers);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
levels.clear();
|
levels.clear();
|
||||||
buffers.clear();
|
buffers.clear();
|
||||||
});
|
});
|
||||||
|
|
@ -829,6 +839,13 @@ mod command_pools {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CommandPools {
|
impl CommandPools {
|
||||||
|
pub fn new(device: Device) -> Self {
|
||||||
|
Self {
|
||||||
|
device,
|
||||||
|
shards: ThreadLocal::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_for_family(&self, family: u32) -> Arc<CommandPool> {
|
pub fn get_for_family(&self, family: u32) -> Arc<CommandPool> {
|
||||||
let shard = self
|
let shard = self
|
||||||
.shards
|
.shards
|
||||||
|
|
|
||||||
|
|
@ -184,6 +184,10 @@ pub struct Image {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Image {
|
impl Image {
|
||||||
|
pub(crate) fn desc(&self) -> &ImageDesc {
|
||||||
|
&self.desc
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new(device: Device, desc: ImageDesc) -> crate::Result<Self> {
|
pub fn new(device: Device, desc: ImageDesc) -> crate::Result<Self> {
|
||||||
let (image, requirements) = Self::new_raw(device.clone(), &desc)?;
|
let (image, requirements) = Self::new_raw(device.clone(), &desc)?;
|
||||||
let alloc = device.alloc2.lock().allocate(&AllocationCreateDesc {
|
let alloc = device.alloc2.lock().allocate(&AllocationCreateDesc {
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ pub mod sync;
|
||||||
mod text;
|
mod text;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
|
||||||
|
use commands::traits::CommandBufferExt;
|
||||||
use device::Device;
|
use device::Device;
|
||||||
|
|
||||||
mod texture {
|
mod texture {
|
||||||
|
|
@ -91,6 +92,8 @@ pub enum Error {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Result(#[from] ash::vk::Result),
|
Result(#[from] ash::vk::Result),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
|
CommandPool(#[from] commands::CommandPoolError),
|
||||||
|
#[error(transparent)]
|
||||||
CStrError(#[from] core::ffi::c_str::FromBytesUntilNulError),
|
CStrError(#[from] core::ffi::c_str::FromBytesUntilNulError),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Utf8Error(#[from] core::str::Utf8Error),
|
Utf8Error(#[from] core::str::Utf8Error),
|
||||||
|
|
@ -925,6 +928,7 @@ impl EguiState {
|
||||||
|
|
||||||
pub struct Renderer2 {
|
pub struct Renderer2 {
|
||||||
pub device: Device,
|
pub device: Device,
|
||||||
|
command_pools: commands::CommandPools,
|
||||||
pub samplers: SamplerCache,
|
pub samplers: SamplerCache,
|
||||||
pub texture_manager: texture::TextureManager,
|
pub texture_manager: texture::TextureManager,
|
||||||
|
|
||||||
|
|
@ -946,6 +950,7 @@ impl Renderer2 {
|
||||||
PhysicalDeviceFeatures {
|
PhysicalDeviceFeatures {
|
||||||
core: vk::PhysicalDeviceFeatures {
|
core: vk::PhysicalDeviceFeatures {
|
||||||
multi_draw_indirect: vk::TRUE,
|
multi_draw_indirect: vk::TRUE,
|
||||||
|
fill_mode_non_solid: vk::TRUE,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
core11: vk::PhysicalDeviceVulkan11Features {
|
core11: vk::PhysicalDeviceVulkan11Features {
|
||||||
|
|
@ -970,6 +975,7 @@ impl Renderer2 {
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
command_pools: commands::CommandPools::new(device.clone()),
|
||||||
samplers: SamplerCache::new(device.clone()),
|
samplers: SamplerCache::new(device.clone()),
|
||||||
texture_manager: texture::TextureManager::new(device.clone()),
|
texture_manager: texture::TextureManager::new(device.clone()),
|
||||||
display,
|
display,
|
||||||
|
|
@ -1047,6 +1053,79 @@ impl Renderer2 {
|
||||||
|
|
||||||
Ok(out)
|
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;
|
pub use vk::Extent2D;
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ use crate::{
|
||||||
device::DeviceOwned,
|
device::DeviceOwned,
|
||||||
render_graph::recorder::{CommandRecorder, SideEffectMap},
|
render_graph::recorder::{CommandRecorder, SideEffectMap},
|
||||||
};
|
};
|
||||||
|
use ash_formats::{FormatExt as _, FormatHelpers as _};
|
||||||
|
|
||||||
use super::resources::*;
|
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
|
// 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,
|
vk::AccessFlags2::TRANSFER_READ,
|
||||||
Some(vk::ImageLayout::TRANSFER_SRC_OPTIMAL),
|
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::PipelineStageFlags2::TRANSFER,
|
||||||
vk::AccessFlags2::TRANSFER_WRITE,
|
vk::AccessFlags2::TRANSFER_WRITE,
|
||||||
Some(vk::ImageLayout::TRANSFER_DST_OPTIMAL),
|
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 {
|
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 struct BeginRendering {
|
||||||
pub color_attachments: Vec<TextureRegion>,
|
pub color_attachments: Vec<RenderingAttachment>,
|
||||||
pub depth_attachment: Option<TextureRegion>,
|
pub depth_attachment: Option<RenderingAttachment>,
|
||||||
pub stencil_attachment: Option<TextureRegion>,
|
pub stencil_attachment: Option<RenderingAttachment>,
|
||||||
pub area: (u32, u32),
|
pub area: (u32, u32),
|
||||||
pub layers: 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 {
|
impl Command for BeginRendering {
|
||||||
fn side_effects(&self, mut map: SideEffectMap) {
|
fn side_effects(&self, mut map: SideEffectMap) {
|
||||||
for attachment in &self.color_attachments {
|
for attachment in &self.color_attachments {
|
||||||
|
|
@ -313,30 +358,107 @@ impl Command for BeginRendering {
|
||||||
// pass instance referencing framebuffer-local stages must contain
|
// pass instance referencing framebuffer-local stages must contain
|
||||||
// only framebuffer-local stages.
|
// only framebuffer-local stages.
|
||||||
|
|
||||||
// TODO: consider loadop and storeop?
|
attachment
|
||||||
attachment.side_effect(map.reborrow().into_side_effect_map2(
|
.region
|
||||||
|
.side_effect(map.reborrow().into_side_effect_map2(
|
||||||
vk::PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT,
|
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),
|
Some(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if let Some(depth) = &self.depth_attachment {
|
if let Some(depth) = &self.depth_attachment {
|
||||||
depth.side_effect(map.reborrow().into_side_effect_map2(
|
depth
|
||||||
vk::PipelineStageFlags2::EARLY_FRAGMENT_TESTS,
|
.region
|
||||||
vk::AccessFlags2::DEPTH_STENCIL_ATTACHMENT_READ,
|
.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),
|
Some(vk::ImageLayout::DEPTH_ATTACHMENT_OPTIMAL),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if let Some(stencil) = &self.stencil_attachment {
|
if let Some(stencil) = &self.stencil_attachment {
|
||||||
stencil.side_effect(map.reborrow().into_side_effect_map2(
|
stencil
|
||||||
vk::PipelineStageFlags2::EARLY_FRAGMENT_TESTS,
|
.region
|
||||||
vk::AccessFlags2::DEPTH_STENCIL_ATTACHMENT_READ,
|
.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),
|
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;
|
pub struct EndRendering;
|
||||||
|
|
@ -346,6 +468,31 @@ impl Command for EndRendering {
|
||||||
// No resource access, but ends the render pass
|
// 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) {}
|
fn apply(self, _recorder: &mut CommandRecorder) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -475,7 +622,20 @@ impl Command for SetViewport {
|
||||||
// No resource access, but affects the viewport state
|
// 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 {
|
pub struct SetScissor {
|
||||||
|
|
@ -490,7 +650,22 @@ impl Command for SetScissor {
|
||||||
// No resource access, but affects the scissor state
|
// 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 {
|
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 {
|
impl Command for PipelineBarrier {
|
||||||
fn side_effects(&self, _map: SideEffectMap) {}
|
fn side_effects(&self, _map: SideEffectMap) {}
|
||||||
|
|
||||||
fn apply(self, _recorder: &mut CommandRecorder) {
|
fn apply(self, recorder: &mut CommandRecorder) {
|
||||||
todo!()
|
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 {
|
mod sealed {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
pub trait IsDrawData {
|
pub trait IsDrawData {
|
||||||
fn side_effects(&self, _map: super::SideEffectMap) {}
|
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 InsideRenderPass {}
|
||||||
pub trait OutsideRenderPass {}
|
pub trait OutsideRenderPass {}
|
||||||
|
|
@ -608,8 +809,47 @@ mod sealed {
|
||||||
use ash::vk;
|
use ash::vk;
|
||||||
pub(super) use sealed::*;
|
pub(super) use sealed::*;
|
||||||
|
|
||||||
impl IsDrawData for DrawData {}
|
impl IsDrawData for DrawData {
|
||||||
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(
|
||||||
|
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 {
|
impl IsDrawData for DrawIndirectData {
|
||||||
fn side_effects(&self, mut map: SideEffectMap) {
|
fn side_effects(&self, mut map: SideEffectMap) {
|
||||||
self.indirect_buffer
|
self.indirect_buffer
|
||||||
|
|
@ -619,6 +859,24 @@ impl IsDrawData for DrawIndirectData {
|
||||||
None,
|
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 {
|
impl IsDrawData for DrawIndexedIndirectData {
|
||||||
fn side_effects(&self, mut map: SideEffectMap) {
|
fn side_effects(&self, mut map: SideEffectMap) {
|
||||||
|
|
@ -629,9 +887,65 @@ impl IsDrawData for DrawIndexedIndirectData {
|
||||||
None,
|
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 OutsideRenderPass for BeginRendering {}
|
||||||
|
impl<T: Resource> OutsideRenderPass for ImportResource<T> {}
|
||||||
|
impl OutsideRenderPass for PreparePresent {}
|
||||||
impl InsideRenderPass for EndRendering {}
|
impl InsideRenderPass for EndRendering {}
|
||||||
impl<T: Resource, U: Resource> OutsideRenderPass for Copy<T, U> {}
|
impl<T: Resource, U: Resource> OutsideRenderPass for Copy<T, U> {}
|
||||||
impl OutsideRenderPass for ClearTexture {}
|
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 ash::vk;
|
||||||
use derive_more::derive::{Deref, DerefMut};
|
use gpu_allocator::MemoryLocation;
|
||||||
|
use parking_lot::RwLock;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
buffers, images,
|
Device, buffers,
|
||||||
|
device::{AllocationStrategy, QueueFlags},
|
||||||
|
images,
|
||||||
render_graph::{
|
render_graph::{
|
||||||
recorder::CommandList,
|
recorder::CommandList,
|
||||||
resources::{self, ResourceId},
|
resources::{self, ResourceId},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct GraphResourceRegistry {
|
#[derive(Default)]
|
||||||
images: BTreeMap<ResourceId, UnsafeCell<images::Image>>,
|
pub(crate) struct GraphResourceRegistry {
|
||||||
views: BTreeMap<ResourceId, UnsafeCell<images::ImageView>>,
|
pub(crate) images: RwLock<BTreeMap<ResourceId, Arc<images::Image>>>,
|
||||||
buffers: BTreeMap<ResourceId, UnsafeCell<buffers::Buffer>>,
|
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)]
|
pub struct StagingBuffer<'a> {
|
||||||
struct StagingBuffer<'a> {
|
pub id: ResourceId,
|
||||||
id: ResourceId,
|
buffer: Arc<std::cell::UnsafeCell<buffers::Buffer>>,
|
||||||
#[deref]
|
_pd: PhantomData<&'a mut buffers::Buffer>,
|
||||||
#[deref_mut]
|
|
||||||
buffer: &'a mut buffers::Buffer,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deref, DerefMut)]
|
pub struct Buffer<'a> {
|
||||||
struct Buffer<'a> {
|
pub id: ResourceId,
|
||||||
id: ResourceId,
|
buffer: Arc<std::cell::UnsafeCell<buffers::Buffer>>,
|
||||||
#[deref]
|
_pd: PhantomData<&'a mut buffers::Buffer>,
|
||||||
#[deref_mut]
|
}
|
||||||
buffer: &'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> {
|
pub struct GraphBuilder<'r> {
|
||||||
_pd: PhantomData<fn(&'r ())>,
|
device: Device,
|
||||||
}
|
registry: Arc<GraphResourceRegistry>,
|
||||||
impl<'r> GraphBuilder<'r> {
|
next_id: Cell<u64>,
|
||||||
fn make_command_list<'cmd>(&self) -> CommandList<'cmd> {
|
_pd: PhantomData<fn(&'r mut ())>,
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn make_staging_buffer(&self, _size: u64) -> StagingBuffer<'r> {
|
|
||||||
todo!()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_buffer(
|
enum GraphResource {
|
||||||
|
Buffer(buffers::Buffer, Option<u32>),
|
||||||
|
SharedBuffer(Arc<buffers::Buffer>, Option<u32>),
|
||||||
|
Image(Arc<images::Image>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'r> GraphBuilder<'r> {
|
||||||
|
pub fn new(device: Device) -> Self {
|
||||||
|
Self {
|
||||||
|
device,
|
||||||
|
registry: Arc::new(GraphResourceRegistry::default()),
|
||||||
|
next_id: Cell::new(0),
|
||||||
|
_pd: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
&self,
|
||||||
_size: u64,
|
size: u64,
|
||||||
_usage: vk::BufferUsageFlags,
|
usage: vk::BufferUsageFlags,
|
||||||
_location: gpu_allocator::MemoryLocation,
|
location: MemoryLocation,
|
||||||
) -> Buffer<'r> {
|
) -> 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
|
// we need to know which queue currently owns this buffer to be able to
|
||||||
// transfer it to the correct queue family when using it
|
// transfer it to the correct queue family when using it
|
||||||
fn import_buffer(&self, _buffer: buffers::Buffer, _owning_queue: Option<u32>) -> Buffer<'r> {
|
pub fn import_buffer(&self, buffer: buffers::Buffer, owning_queue: Option<u32>) -> Buffer<'r> {
|
||||||
todo!()
|
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(
|
let mut pass_cmd = cmd.begin_rendering(
|
||||||
vec![resources::TextureRegion {
|
vec![commands::RenderingAttachment {
|
||||||
|
region: resources::TextureRegion {
|
||||||
texture: resources::Texture(render_target),
|
texture: resources::Texture(render_target),
|
||||||
range: resources::TextureRange {
|
range: resources::TextureRange {
|
||||||
aspect: vk::ImageAspectFlags::COLOR,
|
aspect: vk::ImageAspectFlags::COLOR,
|
||||||
|
|
@ -240,6 +411,12 @@ mod api_demo {
|
||||||
origin: (0, 0, 0),
|
origin: (0, 0, 0),
|
||||||
extent: (extent.0, extent.1, 1),
|
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,
|
||||||
None,
|
None,
|
||||||
|
|
@ -262,9 +439,6 @@ mod api_demo {
|
||||||
max_depth: 1.0,
|
max_depth: 1.0,
|
||||||
});
|
});
|
||||||
|
|
||||||
fn factory<T>() -> T {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
pass_cmd.push(commands::BindPipeline(
|
pass_cmd.push(commands::BindPipeline(
|
||||||
pipeline.raw(),
|
pipeline.raw(),
|
||||||
pipeline.bind_point(),
|
pipeline.bind_point(),
|
||||||
|
|
|
||||||
|
|
@ -7,3 +7,15 @@ mod commands;
|
||||||
mod graph_builder;
|
mod graph_builder;
|
||||||
mod recorder;
|
mod recorder;
|
||||||
mod resources;
|
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,
|
marker::PhantomData,
|
||||||
mem::{MaybeUninit, size_of},
|
mem::{MaybeUninit, size_of},
|
||||||
ptr::NonNull,
|
ptr::NonNull,
|
||||||
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
use ash::vk;
|
use ash::vk;
|
||||||
|
use ash_formats::FormatExt;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
commands::CommandBuffer,
|
commands::CommandBuffer,
|
||||||
images::ImageDesc,
|
images::{ImageDesc, ImageViewDesc},
|
||||||
render_graph::{
|
render_graph::{
|
||||||
commands::{Command, InsideRenderPass, OutsideRenderPass},
|
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 {
|
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 {
|
pub fn cmd_buffer(&self) -> &CommandBuffer {
|
||||||
unimplemented!()
|
&self.cmd_buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_buffer_handle(&self, _id: ResourceId) -> vk::Buffer {
|
pub fn into_command_buffer(self) -> CommandBuffer {
|
||||||
unimplemented!()
|
self.cmd_buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_image_desc(&self, _id: ResourceId) -> &ImageDesc {
|
pub fn get_buffer_handle(&self, id: ResourceId) -> vk::Buffer {
|
||||||
unimplemented!()
|
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 {
|
pub fn get_image_desc(&self, id: ResourceId) -> ImageDesc {
|
||||||
unimplemented!()
|
self.registry
|
||||||
|
.images
|
||||||
|
.read()
|
||||||
|
.get(&id)
|
||||||
|
.expect("unknown graph image")
|
||||||
|
.desc()
|
||||||
|
.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_image_layout(&self, _id: ResourceId) -> vk::ImageLayout {
|
pub fn get_image_handle(&self, id: ResourceId) -> vk::Image {
|
||||||
unimplemented!()
|
self.registry
|
||||||
|
.images
|
||||||
|
.read()
|
||||||
|
.get(&id)
|
||||||
|
.expect("unknown graph image")
|
||||||
|
.raw()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_image_view_handle(&self, _id: ResourceId) -> vk::ImageView {
|
pub fn get_image_layout(&self, id: ResourceId) -> vk::ImageLayout {
|
||||||
unimplemented!()
|
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(
|
pub fn begin_rendering(
|
||||||
&mut self,
|
&mut self,
|
||||||
color_attachments: Vec<TextureRegion>,
|
color_attachments: Vec<super::commands::RenderingAttachment>,
|
||||||
depth_attachment: Option<TextureRegion>,
|
depth_attachment: Option<super::commands::RenderingAttachment>,
|
||||||
stencil_attachment: Option<TextureRegion>,
|
stencil_attachment: Option<super::commands::RenderingAttachment>,
|
||||||
area: (u32, u32),
|
area: (u32, u32),
|
||||||
layers: u32,
|
layers: u32,
|
||||||
) -> RenderPass<'cmd, '_> {
|
) -> RenderPass<'cmd, '_> {
|
||||||
|
|
@ -219,7 +306,7 @@ impl<'cmd> CommandList<'cmd> {
|
||||||
let packed = Packed {
|
let packed = Packed {
|
||||||
meta: CommandMeta {
|
meta: CommandMeta {
|
||||||
apply_command: |recorder, command_ptr, cursor| {
|
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() };
|
let command = unsafe { command_ptr.cast::<C>().read_unaligned() };
|
||||||
C::apply(command, recorder);
|
C::apply(command, recorder);
|
||||||
},
|
},
|
||||||
|
|
@ -232,10 +319,14 @@ impl<'cmd> CommandList<'cmd> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let bytes = self.command_bytes.as_mut_ptr().add(len);
|
let bytes = self.command_bytes.as_mut_ptr().add(len);
|
||||||
bytes.cast::<Packed<C>>().write_unaligned(packed);
|
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;
|
// 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
|
// 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,
|
resource_id: ResourceId,
|
||||||
command_index: u32,
|
command_index: u32,
|
||||||
from: ResourceAccess,
|
from: ResourceAccess,
|
||||||
to: usize,
|
to: ResourceAccess,
|
||||||
}
|
}
|
||||||
|
|
||||||
// map to keep track of the current state of each subresource
|
// 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.
|
// a barrier before the current command index.
|
||||||
|
|
||||||
let mut folded = false;
|
let mut folded = false;
|
||||||
let len = entry.len();
|
|
||||||
for (_i, (sub, _state)) in &mut entry.iter_mut().enumerate().rev() {
|
for (_i, (sub, _state)) in &mut entry.iter_mut().enumerate().rev() {
|
||||||
if !folded && sub.try_fold(&access) {
|
if !folded && sub.try_fold(&access) {
|
||||||
access = *sub;
|
access = *sub;
|
||||||
|
|
@ -306,13 +396,11 @@ impl<'cmd> CommandList<'cmd> {
|
||||||
// conflicting access must be of opposite polarity (or a
|
// conflicting access must be of opposite polarity (or a
|
||||||
// layout transition) and no more accesses can be folded
|
// layout transition) and no more accesses can be folded
|
||||||
// into it.
|
// into it.
|
||||||
// we store the index of `to`, as more accesses may be
|
|
||||||
// folded into it, expanding mips/array layers.
|
|
||||||
barriers.push(Barrier {
|
barriers.push(Barrier {
|
||||||
resource_id: *id,
|
resource_id: *id,
|
||||||
command_index: *command_index,
|
command_index: *command_index,
|
||||||
from: *sub,
|
from: *sub,
|
||||||
to: len,
|
to: access,
|
||||||
});
|
});
|
||||||
|
|
||||||
// we can stop iterating over previous accesses, as we
|
// we can stop iterating over previous accesses, as we
|
||||||
|
|
@ -335,23 +423,83 @@ impl<'cmd> CommandList<'cmd> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// construct barriers
|
let mut result = Vec::with_capacity(barriers.len());
|
||||||
// TODO: need to know each images aspects at this point
|
for barrier in barriers {
|
||||||
for _barrier in barriers {
|
let pipeline_barrier = match (&barrier.from.range, &barrier.to.range) {
|
||||||
// stuff
|
(
|
||||||
|
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!()
|
pub(crate) fn record_commands(&self, recorder: &mut CommandRecorder) {
|
||||||
}
|
|
||||||
|
|
||||||
fn record_commands(&self, recorder: &mut CommandRecorder) {
|
|
||||||
let mut cursor = 0;
|
let mut cursor = 0;
|
||||||
let mut barriers = self.fold_side_effects();
|
let mut barriers = self.fold_side_effects(recorder);
|
||||||
let mut command_index = 0;
|
let mut command_index = 0;
|
||||||
|
|
||||||
while cursor < self.command_bytes.len() {
|
while cursor < self.command_bytes.len() {
|
||||||
barriers
|
barriers
|
||||||
.drain(barriers.partition_point(|(idx, _)| *idx <= command_index)..)
|
.drain(..barriers.partition_point(|(idx, _)| *idx <= command_index))
|
||||||
.for_each(|(_, barrier)| {
|
.for_each(|(_, barrier)| {
|
||||||
barrier.apply(recorder);
|
barrier.apply(recorder);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,10 @@ use crate::{
|
||||||
images::ImageViewDesc,
|
images::ImageViewDesc,
|
||||||
pipeline,
|
pipeline,
|
||||||
render_graph::{
|
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,
|
sync,
|
||||||
util::Rgba8,
|
util::Rgba8,
|
||||||
|
|
@ -164,14 +167,14 @@ impl Wireframe {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let pipeline = Self::create_pipeline(dev.clone())?;
|
||||||
|
|
||||||
let future = cmd.submit_async(
|
let future = cmd.submit_async(
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
Arc::new(sync::Fence::from_pool(&dev.pools.fences, None)?),
|
Arc::new(sync::Fence::from_pool(&dev.pools.fences, None)?),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let pipeline = Self::create_pipeline(dev.clone())?;
|
|
||||||
|
|
||||||
future.await;
|
future.await;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
|
@ -212,13 +215,13 @@ impl Wireframe {
|
||||||
flags: vk::PipelineShaderStageCreateFlags::empty(),
|
flags: vk::PipelineShaderStageCreateFlags::empty(),
|
||||||
module: &shader,
|
module: &shader,
|
||||||
stage: vk::ShaderStageFlags::FRAGMENT,
|
stage: vk::ShaderStageFlags::FRAGMENT,
|
||||||
entry: c"main".into(),
|
entry: c"fragment".into(),
|
||||||
},
|
},
|
||||||
pipeline::ShaderStageDesc {
|
pipeline::ShaderStageDesc {
|
||||||
flags: vk::PipelineShaderStageCreateFlags::empty(),
|
flags: vk::PipelineShaderStageCreateFlags::empty(),
|
||||||
module: &shader,
|
module: &shader,
|
||||||
stage: vk::ShaderStageFlags::VERTEX,
|
stage: vk::ShaderStageFlags::VERTEX,
|
||||||
entry: c"main".into(),
|
entry: c"vertex".into(),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
render_pass: None,
|
render_pass: None,
|
||||||
|
|
@ -380,4 +383,136 @@ impl Wireframe {
|
||||||
});
|
});
|
||||||
Ok(())
|
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