renderer: thread-local commandpools
This commit is contained in:
parent
4cbf1f053b
commit
2b09a2c4f8
|
@ -22,12 +22,14 @@ tracing-subscriber = {version ="0.3.18", features = ["env-filter"]}
|
||||||
glam = {version = "0.29.0", features = ["bytemuck"]}
|
glam = {version = "0.29.0", features = ["bytemuck"]}
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
bitflags = "2.6"
|
bitflags = "2.6"
|
||||||
|
thread_local = "1.1.8"
|
||||||
|
|
||||||
ash = "0.38.0"
|
ash = "0.38.0"
|
||||||
ash-window = "0.13.0"
|
ash-window = "0.13.0"
|
||||||
vk-mem = "0.4.0"
|
vk-mem = "0.4.0"
|
||||||
vk-sync = "0.1.6"
|
vk-sync = "0.1.6"
|
||||||
|
|
||||||
|
arrayvec = "0.7.6"
|
||||||
tinyvec = "1.8"
|
tinyvec = "1.8"
|
||||||
indexmap = "2"
|
indexmap = "2"
|
||||||
petgraph = "0.7"
|
petgraph = "0.7"
|
||||||
|
|
|
@ -5,6 +5,7 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tinyvec = { workspace = true }
|
tinyvec = { workspace = true }
|
||||||
|
arrayvec = { workspace = true }
|
||||||
indexmap = { workspace = true }
|
indexmap = { workspace = true }
|
||||||
petgraph = { workspace = true }
|
petgraph = { workspace = true }
|
||||||
itertools = { workspace = true }
|
itertools = { workspace = true }
|
||||||
|
@ -13,6 +14,7 @@ rand = { workspace = true }
|
||||||
parking_lot = { workspace = true }
|
parking_lot = { workspace = true }
|
||||||
glam = { workspace = true }
|
glam = { workspace = true }
|
||||||
bitflags = { workspace = true }
|
bitflags = { workspace = true }
|
||||||
|
thread_local = {workspace = true}
|
||||||
|
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
|
|
|
@ -70,19 +70,19 @@ impl SingleUseCommandPool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait CommandBuffer: DeviceOwned<vk::CommandBuffer> {
|
pub trait HasQueue: DeviceOwned<vk::CommandBuffer> {
|
||||||
fn queue(&self) -> &Queue;
|
fn queue(&self) -> &Queue;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CommandBuffer for SingleUseCommand {
|
impl HasQueue for SingleUseCommand {
|
||||||
fn queue(&self) -> &Queue {
|
fn queue(&self) -> &Queue {
|
||||||
&self.pool.queue
|
&self.pool.queue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CommandList<T: CommandBuffer>(pub Vec<T>);
|
pub struct CommandList<T: HasQueue>(pub Vec<T>);
|
||||||
|
|
||||||
impl<T: CommandBuffer> CommandList<T> {
|
impl<T: HasQueue> CommandList<T> {
|
||||||
/// all commands in list must be allocated from the same queue.
|
/// all commands in list must be allocated from the same queue.
|
||||||
pub fn submit<'a>(
|
pub fn submit<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
|
@ -215,6 +215,11 @@ impl SingleUseCommand {
|
||||||
self.state.state()
|
self.state.state()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Safety: commandbuffer must not be accessed from multiple threads at the same time
|
||||||
|
pub unsafe fn buffer(&self) -> vk::CommandBuffer {
|
||||||
|
self.handle()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn end(&self) -> VkResult<()> {
|
pub fn end(&self) -> VkResult<()> {
|
||||||
assert_eq!(self.state(), CommandBufferState::Recording);
|
assert_eq!(self.state(), CommandBufferState::Recording);
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -224,14 +229,115 @@ impl SingleUseCommand {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Safety: commandbuffer must not be accessed from multiple threads at the same time
|
pub fn submit_fence(
|
||||||
pub unsafe fn buffer(&self) -> vk::CommandBuffer {
|
&self,
|
||||||
self.handle()
|
wait: Option<(vk::Semaphore, vk::PipelineStageFlags)>,
|
||||||
|
signal: Option<vk::Semaphore>,
|
||||||
|
fence: Option<vk::Fence>,
|
||||||
|
) -> VkResult<()> {
|
||||||
|
assert_eq!(self.state(), CommandBufferState::Recording);
|
||||||
|
unsafe { self.device().dev().end_command_buffer(self.handle())? };
|
||||||
|
|
||||||
|
let buffers = [self.handle()];
|
||||||
|
let mut submit_info = vk::SubmitInfo::default().command_buffers(&buffers);
|
||||||
|
|
||||||
|
if let Some(semaphore) = signal.as_ref() {
|
||||||
|
// SAFETY: T and [T;1] have the same layout
|
||||||
|
submit_info = submit_info.signal_semaphores(unsafe {
|
||||||
|
core::mem::transmute::<&vk::Semaphore, &[vk::Semaphore; 1]>(semaphore)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if let Some((semaphore, stage)) = wait.as_ref() {
|
||||||
|
submit_info = submit_info
|
||||||
|
.wait_semaphores(core::slice::from_ref(semaphore))
|
||||||
|
.wait_dst_stage_mask(core::slice::from_ref(stage));
|
||||||
}
|
}
|
||||||
|
|
||||||
//pub fn copy_buffer_to_image(&self, image: &Image2D, buffer: &Buffer, )
|
let fence = fence.unwrap_or(vk::Fence::null());
|
||||||
|
self.pool.queue().with_locked(|queue| unsafe {
|
||||||
|
self.device()
|
||||||
|
.dev()
|
||||||
|
.queue_submit(queue, &[submit_info], fence)
|
||||||
|
})?;
|
||||||
|
|
||||||
pub fn image_barrier(
|
self.state.set_pending();
|
||||||
|
|
||||||
|
tracing::trace!(
|
||||||
|
"submitted queue {:?} and fence {:?}",
|
||||||
|
self.pool.queue(),
|
||||||
|
fence
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn submit_async<'a>(
|
||||||
|
&'a self,
|
||||||
|
wait: Option<(vk::Semaphore, vk::PipelineStageFlags)>,
|
||||||
|
signal: Option<vk::Semaphore>,
|
||||||
|
fence: Arc<sync::Fence>,
|
||||||
|
) -> VkResult<FenceFuture<'a>> {
|
||||||
|
self.submit_fence(wait, signal, Some(fence.fence()))?;
|
||||||
|
|
||||||
|
Ok(FenceFuture::new(fence))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn submit_blocking(
|
||||||
|
self,
|
||||||
|
wait: Option<(vk::Semaphore, vk::PipelineStageFlags)>,
|
||||||
|
signal: Option<vk::Semaphore>,
|
||||||
|
) -> VkResult<()> {
|
||||||
|
let fence = Arc::new(sync::Fence::create(self.device().clone())?);
|
||||||
|
let future = self.submit_async(wait, signal, fence)?;
|
||||||
|
future.block()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl traits::CommandBufferExt for SingleUseCommand {}
|
||||||
|
|
||||||
|
pub mod traits {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub trait CommandBufferExt: DeviceOwned<vk::CommandBuffer> {
|
||||||
|
fn submit_to_queue(
|
||||||
|
self,
|
||||||
|
queue: &Queue,
|
||||||
|
wait: Option<(vk::Semaphore, vk::PipelineStageFlags)>,
|
||||||
|
signal: Option<vk::Semaphore>,
|
||||||
|
fence: Arc<sync::Fence>,
|
||||||
|
) -> VkResult<impl std::future::Future<Output = ()>>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
//assert_eq!(self.state(), CommandBufferState::Recording);
|
||||||
|
unsafe { self.device().dev().end_command_buffer(self.handle())? };
|
||||||
|
|
||||||
|
let buffers = [self.handle()];
|
||||||
|
let mut submit_info = vk::SubmitInfo::default().command_buffers(&buffers);
|
||||||
|
|
||||||
|
if let Some(semaphore) = signal.as_ref() {
|
||||||
|
submit_info = submit_info.signal_semaphores(core::slice::from_ref(semaphore));
|
||||||
|
}
|
||||||
|
if let Some((semaphore, stage)) = wait.as_ref() {
|
||||||
|
submit_info = submit_info
|
||||||
|
.wait_semaphores(core::slice::from_ref(semaphore))
|
||||||
|
.wait_dst_stage_mask(core::slice::from_ref(stage));
|
||||||
|
}
|
||||||
|
|
||||||
|
queue.with_locked(|queue| unsafe {
|
||||||
|
self.device()
|
||||||
|
.dev()
|
||||||
|
.queue_submit(queue, &[submit_info], fence.fence())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
tracing::trace!("submitted queue {:?} and fence {:?}", queue, fence);
|
||||||
|
|
||||||
|
Ok(crate::sync::FenceFuture::new(fence))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn image_barrier(
|
||||||
&self,
|
&self,
|
||||||
image: vk::Image,
|
image: vk::Image,
|
||||||
aspects: vk::ImageAspectFlags,
|
aspects: vk::ImageAspectFlags,
|
||||||
|
@ -243,7 +349,8 @@ impl SingleUseCommand {
|
||||||
new_layout: vk::ImageLayout,
|
new_layout: vk::ImageLayout,
|
||||||
queue_ownership_op: Option<QueueOwnership>,
|
queue_ownership_op: Option<QueueOwnership>,
|
||||||
) {
|
) {
|
||||||
assert_eq!(self.state(), CommandBufferState::Recording);
|
// assert_eq!(self.state(), CommandBufferState::Recording);
|
||||||
|
|
||||||
let (src_family, dst_family) = queue_ownership_op
|
let (src_family, dst_family) = queue_ownership_op
|
||||||
.map(|t| (t.src, t.dst))
|
.map(|t| (t.src, t.dst))
|
||||||
.unwrap_or((vk::QUEUE_FAMILY_IGNORED, vk::QUEUE_FAMILY_IGNORED));
|
.unwrap_or((vk::QUEUE_FAMILY_IGNORED, vk::QUEUE_FAMILY_IGNORED));
|
||||||
|
@ -277,17 +384,17 @@ impl SingleUseCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn blit_images(
|
fn blit_images(
|
||||||
&self,
|
&self,
|
||||||
src: &Image,
|
src: &Image,
|
||||||
src_region: util::Rect2D,
|
src_region: util::Rect2D,
|
||||||
dst: &Image,
|
dst: &Image,
|
||||||
dst_region: util::Rect2D,
|
dst_region: util::Rect2D,
|
||||||
) {
|
) {
|
||||||
assert_eq!(self.state(), CommandBufferState::Recording);
|
// assert_eq!(self.state(), CommandBufferState::Recording);
|
||||||
unsafe {
|
unsafe {
|
||||||
self.device().dev().cmd_blit_image(
|
self.device().dev().cmd_blit_image(
|
||||||
self.buffer(),
|
self.handle(),
|
||||||
src.image(),
|
src.image(),
|
||||||
vk::ImageLayout::TRANSFER_SRC_OPTIMAL,
|
vk::ImageLayout::TRANSFER_SRC_OPTIMAL,
|
||||||
dst.image(),
|
dst.image(),
|
||||||
|
@ -310,14 +417,14 @@ impl SingleUseCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn copy_buffer_to_image(
|
fn copy_buffer_to_image(
|
||||||
&self,
|
&self,
|
||||||
buffer: vk::Buffer,
|
buffer: vk::Buffer,
|
||||||
image: vk::Image,
|
image: vk::Image,
|
||||||
layout: vk::ImageLayout,
|
layout: vk::ImageLayout,
|
||||||
regions: &[vk::BufferImageCopy],
|
regions: &[vk::BufferImageCopy],
|
||||||
) {
|
) {
|
||||||
assert_eq!(self.state(), CommandBufferState::Recording);
|
// assert_eq!(self.state(), CommandBufferState::Recording);
|
||||||
unsafe {
|
unsafe {
|
||||||
self.device().dev().cmd_copy_buffer_to_image(
|
self.device().dev().cmd_copy_buffer_to_image(
|
||||||
self.handle(),
|
self.handle(),
|
||||||
|
@ -329,16 +436,16 @@ impl SingleUseCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn copy_buffers(&self, src: vk::Buffer, dst: vk::Buffer, regions: &[vk::BufferCopy]) {
|
fn copy_buffers(&self, src: vk::Buffer, dst: vk::Buffer, regions: &[vk::BufferCopy]) {
|
||||||
assert_eq!(self.state(), CommandBufferState::Recording);
|
// assert_eq!(self.state(), CommandBufferState::Recording);
|
||||||
unsafe {
|
unsafe {
|
||||||
self.device()
|
self.device()
|
||||||
.dev()
|
.dev()
|
||||||
.cmd_copy_buffer(self.handle(), src, dst, regions);
|
.cmd_copy_buffer(self.handle(), src, dst, regions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn copy_images(
|
fn copy_images(
|
||||||
&self,
|
&self,
|
||||||
src: vk::Image,
|
src: vk::Image,
|
||||||
src_layout: vk::ImageLayout,
|
src_layout: vk::ImageLayout,
|
||||||
|
@ -346,7 +453,7 @@ impl SingleUseCommand {
|
||||||
dst_layout: vk::ImageLayout,
|
dst_layout: vk::ImageLayout,
|
||||||
regions: &[vk::ImageCopy],
|
regions: &[vk::ImageCopy],
|
||||||
) {
|
) {
|
||||||
assert_eq!(self.state(), CommandBufferState::Recording);
|
// assert_eq!(self.state(), CommandBufferState::Recording);
|
||||||
unsafe {
|
unsafe {
|
||||||
self.device().dev().cmd_copy_image(
|
self.device().dev().cmd_copy_image(
|
||||||
self.handle(),
|
self.handle(),
|
||||||
|
@ -359,7 +466,7 @@ impl SingleUseCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear_color_image(
|
fn clear_color_image(
|
||||||
&self,
|
&self,
|
||||||
image: vk::Image,
|
image: vk::Image,
|
||||||
format: vk::Format,
|
format: vk::Format,
|
||||||
|
@ -367,7 +474,7 @@ impl SingleUseCommand {
|
||||||
color: crate::Rgba,
|
color: crate::Rgba,
|
||||||
subresources: &[vk::ImageSubresourceRange],
|
subresources: &[vk::ImageSubresourceRange],
|
||||||
) {
|
) {
|
||||||
assert_eq!(self.state(), CommandBufferState::Recording);
|
// assert_eq!(self.state(), CommandBufferState::Recording);
|
||||||
let clear_colors = match format.get_component_kind() {
|
let clear_colors = match format.get_component_kind() {
|
||||||
crate::util::FormatComponentKind::Float => vk::ClearColorValue {
|
crate::util::FormatComponentKind::Float => vk::ClearColorValue {
|
||||||
float32: color.into_f32(),
|
float32: color.into_f32(),
|
||||||
|
@ -391,39 +498,39 @@ impl SingleUseCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn begin_rendering(&self, rendering_info: vk::RenderingInfo<'_>) {
|
fn begin_rendering(&self, rendering_info: vk::RenderingInfo<'_>) {
|
||||||
assert_eq!(self.state(), CommandBufferState::Recording);
|
// assert_eq!(self.state(), CommandBufferState::Recording);
|
||||||
unsafe {
|
unsafe {
|
||||||
self.device()
|
self.device()
|
||||||
.dev()
|
.dev()
|
||||||
.cmd_begin_rendering(self.buffer(), &rendering_info);
|
.cmd_begin_rendering(self.handle(), &rendering_info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_viewport(&self, viewports: &[vk::Viewport]) {
|
fn set_viewport(&self, viewports: &[vk::Viewport]) {
|
||||||
assert_eq!(self.state(), CommandBufferState::Recording);
|
// assert_eq!(self.state(), CommandBufferState::Recording);
|
||||||
unsafe {
|
unsafe {
|
||||||
self.device()
|
self.device()
|
||||||
.dev()
|
.dev()
|
||||||
.cmd_set_viewport(self.buffer(), 0, viewports);
|
.cmd_set_viewport(self.handle(), 0, viewports);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn set_scissors(&self, scissors: &[vk::Rect2D]) {
|
fn set_scissors(&self, scissors: &[vk::Rect2D]) {
|
||||||
assert_eq!(self.state(), CommandBufferState::Recording);
|
// assert_eq!(self.state(), CommandBufferState::Recording);
|
||||||
unsafe {
|
unsafe {
|
||||||
self.device()
|
self.device()
|
||||||
.dev()
|
.dev()
|
||||||
.cmd_set_scissor(self.buffer(), 0, scissors);
|
.cmd_set_scissor(self.handle(), 0, scissors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn push_constants(
|
fn push_constants(
|
||||||
&self,
|
&self,
|
||||||
layout: &PipelineLayout,
|
layout: &PipelineLayout,
|
||||||
stage: vk::ShaderStageFlags,
|
stage: vk::ShaderStageFlags,
|
||||||
offset: u32,
|
offset: u32,
|
||||||
bytes: &[u8],
|
bytes: &[u8],
|
||||||
) {
|
) {
|
||||||
assert_eq!(self.state(), CommandBufferState::Recording);
|
// assert_eq!(self.state(), CommandBufferState::Recording);
|
||||||
unsafe {
|
unsafe {
|
||||||
self.device().dev().cmd_push_constants(
|
self.device().dev().cmd_push_constants(
|
||||||
self.handle(),
|
self.handle(),
|
||||||
|
@ -434,44 +541,44 @@ impl SingleUseCommand {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn bind_pipeline(&self, pipeline: &Pipeline) {
|
fn bind_pipeline(&self, pipeline: &Pipeline) {
|
||||||
assert_eq!(self.state(), CommandBufferState::Recording);
|
// assert_eq!(self.state(), CommandBufferState::Recording);
|
||||||
unsafe {
|
unsafe {
|
||||||
self.device().dev().cmd_bind_pipeline(
|
self.device().dev().cmd_bind_pipeline(
|
||||||
self.buffer(),
|
self.handle(),
|
||||||
pipeline.bind_point(),
|
pipeline.bind_point(),
|
||||||
pipeline.handle(),
|
pipeline.handle(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn bind_vertex_buffers(&self, buffers: &[vk::Buffer], offsets: &[u64]) {
|
fn bind_vertex_buffers(&self, buffers: &[vk::Buffer], offsets: &[u64]) {
|
||||||
assert_eq!(self.state(), CommandBufferState::Recording);
|
// assert_eq!(self.state(), CommandBufferState::Recording);
|
||||||
unsafe {
|
unsafe {
|
||||||
self.device()
|
self.device()
|
||||||
.dev()
|
.dev()
|
||||||
.cmd_bind_vertex_buffers(self.buffer(), 0, buffers, offsets);
|
.cmd_bind_vertex_buffers(self.handle(), 0, buffers, offsets);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn bind_indices(&self, buffer: vk::Buffer, offset: u64, kind: vk::IndexType) {
|
fn bind_indices(&self, buffer: vk::Buffer, offset: u64, kind: vk::IndexType) {
|
||||||
assert_eq!(self.state(), CommandBufferState::Recording);
|
// assert_eq!(self.state(), CommandBufferState::Recording);
|
||||||
unsafe {
|
unsafe {
|
||||||
self.device()
|
self.device()
|
||||||
.dev()
|
.dev()
|
||||||
.cmd_bind_index_buffer(self.buffer(), buffer, offset, kind);
|
.cmd_bind_index_buffer(self.handle(), buffer, offset, kind);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bind_descriptor_sets(
|
fn bind_descriptor_sets(
|
||||||
&self,
|
&self,
|
||||||
layout: &PipelineLayout,
|
layout: &PipelineLayout,
|
||||||
bind_point: vk::PipelineBindPoint,
|
bind_point: vk::PipelineBindPoint,
|
||||||
descriptor_sets: &[vk::DescriptorSet],
|
descriptor_sets: &[vk::DescriptorSet],
|
||||||
) {
|
) {
|
||||||
assert_eq!(self.state(), CommandBufferState::Recording);
|
// assert_eq!(self.state(), CommandBufferState::Recording);
|
||||||
use crate::device::DeviceOwned;
|
use crate::device::DeviceOwned;
|
||||||
unsafe {
|
unsafe {
|
||||||
self.device().dev().cmd_bind_descriptor_sets(
|
self.device().dev().cmd_bind_descriptor_sets(
|
||||||
self.buffer(),
|
self.handle(),
|
||||||
bind_point,
|
bind_point,
|
||||||
layout.handle(),
|
layout.handle(),
|
||||||
0,
|
0,
|
||||||
|
@ -481,8 +588,7 @@ impl SingleUseCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
fn draw_indexed(
|
||||||
pub fn draw_indexed(
|
|
||||||
&self,
|
&self,
|
||||||
indices: u32,
|
indices: u32,
|
||||||
instances: u32,
|
instances: u32,
|
||||||
|
@ -490,10 +596,10 @@ impl SingleUseCommand {
|
||||||
vertex_offset: i32,
|
vertex_offset: i32,
|
||||||
instance_offset: u32,
|
instance_offset: u32,
|
||||||
) {
|
) {
|
||||||
assert_eq!(self.state(), CommandBufferState::Recording);
|
// assert_eq!(self.state(), CommandBufferState::Recording);
|
||||||
unsafe {
|
unsafe {
|
||||||
self.device().dev().cmd_draw_indexed(
|
self.device().dev().cmd_draw_indexed(
|
||||||
self.buffer(),
|
self.handle(),
|
||||||
indices,
|
indices,
|
||||||
instances,
|
instances,
|
||||||
index_offset,
|
index_offset,
|
||||||
|
@ -503,11 +609,11 @@ impl SingleUseCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_indexed_indirect(&self, buffer: vk::Buffer, offset: u64, count: u32, stride: u32) {
|
fn draw_indexed_indirect(&self, buffer: vk::Buffer, offset: u64, count: u32, stride: u32) {
|
||||||
assert_eq!(self.state(), CommandBufferState::Recording);
|
// assert_eq!(self.state(), CommandBufferState::Recording);
|
||||||
unsafe {
|
unsafe {
|
||||||
self.device().dev().cmd_draw_indexed_indirect(
|
self.device().dev().cmd_draw_indexed_indirect(
|
||||||
self.buffer(),
|
self.handle(),
|
||||||
buffer,
|
buffer,
|
||||||
offset,
|
offset,
|
||||||
count,
|
count,
|
||||||
|
@ -516,30 +622,285 @@ impl SingleUseCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn end_rendering(&self) {
|
fn end_rendering(&self) {
|
||||||
assert_eq!(self.state(), CommandBufferState::Recording);
|
// assert_eq!(self.state(), CommandBufferState::Recording);
|
||||||
unsafe {
|
unsafe {
|
||||||
self.device().dev().cmd_end_rendering(self.buffer());
|
self.device().dev().cmd_end_rendering(self.handle());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use command_pools::{CommandBuffer, CommandBufferFuture, CommandPool, CommandPools};
|
||||||
|
|
||||||
|
mod command_pools {
|
||||||
|
use std::{borrow::Cow, cell::Cell, mem::ManuallyDrop, sync::Arc};
|
||||||
|
|
||||||
|
use arrayvec::ArrayVec;
|
||||||
|
use ash::{prelude::VkResult, vk};
|
||||||
|
use parking_lot::Mutex;
|
||||||
|
use smol::future::FutureExt;
|
||||||
|
use thread_local::ThreadLocal;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
define_device_owned_handle,
|
||||||
|
device::{Device, DeviceOwned},
|
||||||
|
sync,
|
||||||
|
util::MutexExt,
|
||||||
|
Queue,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum Error {
|
||||||
|
#[error(transparent)]
|
||||||
|
Vulkan(#[from] vk::Result),
|
||||||
|
#[error("CommandBuffer allocation failed: wrong number of command buffers.")]
|
||||||
|
NoCommandBuffersAllocated,
|
||||||
|
#[error("Attempted to submit commandbuffer to incompatible queue.")]
|
||||||
|
InvalidQueueSubmission,
|
||||||
|
}
|
||||||
|
|
||||||
|
define_device_owned_handle! {
|
||||||
|
#[derive()]
|
||||||
|
pub CommandPool(vk::CommandPool) {
|
||||||
|
family: u32,
|
||||||
|
free_list: Mutex<Cell<(Vec<vk::CommandBufferLevel>, Vec<vk::CommandBuffer>)>>,
|
||||||
|
} => |this| unsafe {
|
||||||
|
this.device().dev().destroy_command_pool(this.handle(), None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn submit_fence(
|
impl !Sync for CommandPool {}
|
||||||
|
|
||||||
|
impl CommandPool {
|
||||||
|
pub fn new(
|
||||||
|
dev: Device,
|
||||||
|
family: u32,
|
||||||
|
flags: vk::CommandPoolCreateFlags,
|
||||||
|
name: Option<Cow<'static, str>>,
|
||||||
|
) -> Result<CommandPool, vk::Result> {
|
||||||
|
let pool_info = vk::CommandPoolCreateInfo::default()
|
||||||
|
.queue_family_index(family)
|
||||||
|
.flags(flags);
|
||||||
|
|
||||||
|
let pool = unsafe { dev.dev().create_command_pool(&pool_info, None)? };
|
||||||
|
|
||||||
|
Self::construct(dev, pool, name, family, Mutex::new(Default::default()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub unsafe fn reset(&self) -> VkResult<()> {
|
||||||
|
unsafe {
|
||||||
|
self.device()
|
||||||
|
.dev()
|
||||||
|
.reset_command_pool(self.handle(), vk::CommandPoolResetFlags::empty())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn free_command_buffers(&self) {
|
||||||
|
self.free_list_mut(|levels, buffers| {
|
||||||
|
unsafe {
|
||||||
|
self.device()
|
||||||
|
.dev()
|
||||||
|
.free_command_buffers(self.handle(), &buffers);
|
||||||
|
}
|
||||||
|
levels.clear();
|
||||||
|
buffers.clear();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn free_command_buffer(
|
||||||
|
self: &Arc<Self>,
|
||||||
|
level: vk::CommandBufferLevel,
|
||||||
|
buffer: vk::CommandBuffer,
|
||||||
|
) {
|
||||||
|
self.free_list_mut(|levels, buffers| {
|
||||||
|
levels.push(level);
|
||||||
|
buffers.push(buffer);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn free_list_mut<
|
||||||
|
T,
|
||||||
|
F: FnOnce(&mut Vec<vk::CommandBufferLevel>, &mut Vec<vk::CommandBuffer>) -> T,
|
||||||
|
>(
|
||||||
&self,
|
&self,
|
||||||
|
cb: F,
|
||||||
|
) -> T {
|
||||||
|
self.free_list.with_locked(|cell| {
|
||||||
|
let (mut levels, mut buffers) = cell.take();
|
||||||
|
let t = cb(&mut levels, &mut buffers);
|
||||||
|
cell.set((levels, buffers));
|
||||||
|
t
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn alloc(
|
||||||
|
self: &Arc<Self>,
|
||||||
|
name: Option<Cow<'static, str>>,
|
||||||
|
level: vk::CommandBufferLevel,
|
||||||
|
flags: vk::CommandBufferUsageFlags,
|
||||||
|
) -> Result<CommandBuffer, Error> {
|
||||||
|
// TODO: is this really the right place?
|
||||||
|
self.free_command_buffers();
|
||||||
|
|
||||||
|
let handle = {
|
||||||
|
let info = vk::CommandBufferAllocateInfo::default()
|
||||||
|
.command_pool(self.handle())
|
||||||
|
.command_buffer_count(1)
|
||||||
|
.level(level);
|
||||||
|
|
||||||
|
unsafe { self.device().dev().allocate_command_buffers(&info) }?
|
||||||
|
.pop()
|
||||||
|
.ok_or(Error::NoCommandBuffersAllocated)?
|
||||||
|
};
|
||||||
|
|
||||||
|
let begin_info = vk::CommandBufferBeginInfo::default().flags(flags);
|
||||||
|
unsafe {
|
||||||
|
self.device()
|
||||||
|
.dev()
|
||||||
|
.begin_command_buffer(handle, &begin_info)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(CommandBuffer::construct(
|
||||||
|
self.device().clone(),
|
||||||
|
handle,
|
||||||
|
name,
|
||||||
|
self.clone(),
|
||||||
|
level,
|
||||||
|
)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn alloc_primary(
|
||||||
|
self: &Arc<Self>,
|
||||||
|
name: Option<Cow<'static, str>>,
|
||||||
|
flags: vk::CommandBufferUsageFlags,
|
||||||
|
) -> Result<CommandBuffer, Error> {
|
||||||
|
self.alloc(name, vk::CommandBufferLevel::PRIMARY, flags)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::fmt::Debug for CommandPool {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("CommandPool")
|
||||||
|
.field("inner", &self.inner)
|
||||||
|
.field("family", &self.family)
|
||||||
|
.finish_non_exhaustive()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CommandPoolsShard {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
per_family_pools: ArrayVec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_or_default(&mut self, dev: &Device, family: u32) -> Arc<CommandPool> {
|
||||||
|
match self
|
||||||
|
.per_family_pools
|
||||||
|
.binary_search_by_key(&family, |entry| entry.0)
|
||||||
|
{
|
||||||
|
Ok(i) => self.per_family_pools[i].1.clone(),
|
||||||
|
Err(i) => {
|
||||||
|
let pool = Arc::new(
|
||||||
|
CommandPool::new(
|
||||||
|
dev.clone(),
|
||||||
|
family,
|
||||||
|
vk::CommandPoolCreateFlags::empty(),
|
||||||
|
Some(
|
||||||
|
format!("commandpool-{:?}-{}", std::thread::current().id(), family)
|
||||||
|
.into(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
self.per_family_pools
|
||||||
|
.try_insert(i, (family, pool.clone()))
|
||||||
|
.expect("too many command pools in thread local");
|
||||||
|
|
||||||
|
pool
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAFETY: this isn't safe lmao. assumes pools are unused when dropped
|
||||||
|
unsafe impl Send for CommandPoolsShard {}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct CommandPoolsShard {
|
||||||
|
per_family_pools: ArrayVec<(u32, Arc<CommandPool>), 8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CommandPools {
|
||||||
|
device: Device,
|
||||||
|
shards: ThreadLocal<crossbeam::utils::CachePadded<core::cell::Cell<CommandPoolsShard>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CommandPools {
|
||||||
|
pub fn get_for_family(&self, family: u32) -> Arc<CommandPool> {
|
||||||
|
let shard = self
|
||||||
|
.shards
|
||||||
|
.get_or(|| core::cell::Cell::new(CommandPoolsShard::new()).into());
|
||||||
|
|
||||||
|
let mut inner = shard.replace(CommandPoolsShard::new());
|
||||||
|
let pool = inner.find_or_default(&self.device, family);
|
||||||
|
_ = shard.replace(inner);
|
||||||
|
|
||||||
|
pool
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn allocate_buffer(
|
||||||
|
&self,
|
||||||
|
family: u32,
|
||||||
|
name: Option<Cow<'static, str>>,
|
||||||
|
level: vk::CommandBufferLevel,
|
||||||
|
flags: vk::CommandBufferUsageFlags,
|
||||||
|
) -> Result<CommandBuffer, Error> {
|
||||||
|
self.get_for_family(family).alloc(name, level, flags)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
define_device_owned_handle! {
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub CommandBuffer(vk::CommandBuffer) {
|
||||||
|
pool: Arc<CommandPool>,
|
||||||
|
#[allow(unused)]
|
||||||
|
level: vk::CommandBufferLevel,
|
||||||
|
} => |this| unsafe {
|
||||||
|
this.device().dev().free_command_buffers(this.pool.handle(), &[this.handle()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl !Sync for CommandBuffer {}
|
||||||
|
impl !Send for CommandBuffer {}
|
||||||
|
|
||||||
|
impl super::traits::CommandBufferExt for CommandBuffer {}
|
||||||
|
|
||||||
|
impl CommandBuffer {
|
||||||
|
pub fn submit(
|
||||||
|
self,
|
||||||
|
queue: &Queue,
|
||||||
wait: Option<(vk::Semaphore, vk::PipelineStageFlags)>,
|
wait: Option<(vk::Semaphore, vk::PipelineStageFlags)>,
|
||||||
signal: Option<vk::Semaphore>,
|
signal: Option<vk::Semaphore>,
|
||||||
fence: Option<vk::Fence>,
|
fence: Arc<sync::Fence>,
|
||||||
) -> VkResult<()> {
|
) -> Result<CommandBufferFuture, Error> {
|
||||||
assert_eq!(self.state(), CommandBufferState::Recording);
|
let this = ManuallyDrop::new(self);
|
||||||
unsafe { self.device().dev().end_command_buffer(self.handle())? };
|
|
||||||
|
|
||||||
let buffers = [self.handle()];
|
if queue.1 != this.pool.family {
|
||||||
|
tracing::error!("attempted to submit commandbuffer to incompatible queue.");
|
||||||
|
return Err(Error::InvalidQueueSubmission);
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
this.device().dev().end_command_buffer(this.handle())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let buffers = [this.handle()];
|
||||||
let mut submit_info = vk::SubmitInfo::default().command_buffers(&buffers);
|
let mut submit_info = vk::SubmitInfo::default().command_buffers(&buffers);
|
||||||
|
|
||||||
if let Some(semaphore) = signal.as_ref() {
|
if let Some(semaphore) = signal.as_ref() {
|
||||||
// SAFETY: T and [T;1] have the same layout
|
submit_info = submit_info.signal_semaphores(core::slice::from_ref(semaphore));
|
||||||
submit_info = submit_info.signal_semaphores(unsafe {
|
|
||||||
core::mem::transmute::<&vk::Semaphore, &[vk::Semaphore; 1]>(semaphore)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
if let Some((semaphore, stage)) = wait.as_ref() {
|
if let Some((semaphore, stage)) = wait.as_ref() {
|
||||||
submit_info = submit_info
|
submit_info = submit_info
|
||||||
|
@ -547,41 +908,48 @@ impl SingleUseCommand {
|
||||||
.wait_dst_stage_mask(core::slice::from_ref(stage));
|
.wait_dst_stage_mask(core::slice::from_ref(stage));
|
||||||
}
|
}
|
||||||
|
|
||||||
let fence = fence.unwrap_or(vk::Fence::null());
|
queue.with_locked(|queue| unsafe {
|
||||||
self.pool.queue().with_locked(|queue| unsafe {
|
this.device()
|
||||||
self.device()
|
|
||||||
.dev()
|
.dev()
|
||||||
.queue_submit(queue, &[submit_info], fence)
|
.queue_submit(queue, &[submit_info], fence.fence())
|
||||||
})?;
|
})?;
|
||||||
tracing::trace!(
|
|
||||||
"submitted queue {:?} and fence {:?}",
|
|
||||||
self.pool.queue(),
|
|
||||||
fence
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(CommandBufferFuture {
|
||||||
|
inner: sync::FenceFuture::new(fence),
|
||||||
|
pool: this.pool.clone(),
|
||||||
|
buffer: this.handle(),
|
||||||
|
level: this.level,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn submit_async<'a>(
|
pub struct CommandBufferFuture {
|
||||||
&'a self,
|
inner: sync::FenceFuture<'static>,
|
||||||
wait: Option<(vk::Semaphore, vk::PipelineStageFlags)>,
|
pool: Arc<CommandPool>,
|
||||||
signal: Option<vk::Semaphore>,
|
buffer: vk::CommandBuffer,
|
||||||
fence: Arc<sync::Fence>,
|
level: vk::CommandBufferLevel,
|
||||||
) -> VkResult<FenceFuture<'a>> {
|
|
||||||
self.submit_fence(wait, signal, Some(fence.fence()))?;
|
|
||||||
|
|
||||||
Ok(FenceFuture::new(fence))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
impl CommandBufferFuture {
|
||||||
pub fn submit_blocking(
|
pub fn block(&self) -> VkResult<()> {
|
||||||
self,
|
self.inner.block()
|
||||||
wait: Option<(vk::Semaphore, vk::PipelineStageFlags)>,
|
}
|
||||||
signal: Option<vk::Semaphore>,
|
}
|
||||||
) -> VkResult<()> {
|
|
||||||
let fence = Arc::new(sync::Fence::create(self.device().clone())?);
|
impl core::future::Future for CommandBufferFuture {
|
||||||
let future = self.submit_async(wait, signal, fence)?;
|
type Output = <sync::FenceFuture<'static> as core::future::Future>::Output;
|
||||||
future.block()?;
|
|
||||||
Ok(())
|
fn poll(
|
||||||
|
mut self: std::pin::Pin<&mut Self>,
|
||||||
|
cx: &mut std::task::Context<'_>,
|
||||||
|
) -> std::task::Poll<Self::Output> {
|
||||||
|
self.inner.poll(cx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for CommandBufferFuture {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.pool.free_command_buffer(self.level, self.buffer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ pub struct DeviceQueueFamilies {
|
||||||
pub(crate) present: (u32, u32),
|
pub(crate) present: (u32, u32),
|
||||||
pub(crate) async_compute: (u32, u32),
|
pub(crate) async_compute: (u32, u32),
|
||||||
pub(crate) transfer: (u32, u32),
|
pub(crate) transfer: (u32, u32),
|
||||||
|
pub(crate) properties: Box<[vk::QueueFamilyProperties]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DeviceQueueFamilies {
|
impl DeviceQueueFamilies {
|
||||||
|
@ -78,12 +79,12 @@ bitflags::bitflags! {
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct QueueFlags: u32 {
|
pub struct QueueFlags: u32 {
|
||||||
const GRAPHICS = 1 << 0;
|
const GRAPHICS = 1 << 0;
|
||||||
const PRESENT = 1 << 1;
|
const ASYNC_COMPUTE = 1 << 1;
|
||||||
const ASYNC_COMPUTE = 1 << 2;
|
const TRANSFER = 1 << 2;
|
||||||
const TRANSFER = 1 << 3;
|
const PRESENT = 1 << 3;
|
||||||
|
|
||||||
const NONE = 0;
|
const NONE = 0;
|
||||||
const PRESENT_GRAPHICS = 1 << 0 | 1 << 1;
|
const PRESENT_GRAPHICS = 1 << 0 | 1 << 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,7 +257,7 @@ impl DeviceBuilder {
|
||||||
display_handle: Option<RawDisplayHandle>,
|
display_handle: Option<RawDisplayHandle>,
|
||||||
pdev: vk::PhysicalDevice,
|
pdev: vk::PhysicalDevice,
|
||||||
) -> DeviceQueueFamilies {
|
) -> DeviceQueueFamilies {
|
||||||
let queue_families = unsafe {
|
let queue_familiy_properties = unsafe {
|
||||||
instance
|
instance
|
||||||
.instance
|
.instance
|
||||||
.get_physical_device_queue_family_properties(pdev)
|
.get_physical_device_queue_family_properties(pdev)
|
||||||
|
@ -318,7 +319,7 @@ impl DeviceBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut queue_families = QueueFamilies(
|
let mut queue_families = QueueFamilies(
|
||||||
queue_families
|
queue_familiy_properties
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, family)| {
|
.map(|(i, family)| {
|
||||||
|
@ -416,6 +417,7 @@ impl DeviceBuilder {
|
||||||
async_compute,
|
async_compute,
|
||||||
transfer,
|
transfer,
|
||||||
present,
|
present,
|
||||||
|
properties: queue_familiy_properties.into_boxed_slice(),
|
||||||
};
|
};
|
||||||
|
|
||||||
queues
|
queues
|
||||||
|
@ -887,7 +889,7 @@ impl Device {
|
||||||
pub fn physical_device(&self) -> &PhysicalDevice {
|
pub fn physical_device(&self) -> &PhysicalDevice {
|
||||||
&self.0.physical
|
&self.0.physical
|
||||||
}
|
}
|
||||||
pub fn graphics_queue(&self) -> &Queue {
|
pub fn main_queue(&self) -> &Queue {
|
||||||
&self.0.main_queue
|
&self.0.main_queue
|
||||||
}
|
}
|
||||||
pub fn transfer_queue(&self) -> &Queue {
|
pub fn transfer_queue(&self) -> &Queue {
|
||||||
|
|
|
@ -5,6 +5,7 @@ use indexmap::IndexMap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
buffers::{Buffer, BufferDesc},
|
buffers::{Buffer, BufferDesc},
|
||||||
|
commands::traits::CommandBufferExt,
|
||||||
device::{self, DeviceOwned},
|
device::{self, DeviceOwned},
|
||||||
images::{Image, ImageDesc, ImageViewDesc},
|
images::{Image, ImageDesc, ImageViewDesc},
|
||||||
render_graph::{
|
render_graph::{
|
||||||
|
|
|
@ -8,7 +8,8 @@ use std::{
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
buffers::{Buffer, BufferDesc},
|
buffers::{Buffer, BufferDesc},
|
||||||
commands, def_monotonic_id,
|
commands::{self, traits::CommandBufferExt},
|
||||||
|
def_monotonic_id,
|
||||||
device::{self, DeviceOwned},
|
device::{self, DeviceOwned},
|
||||||
images::{self, Image, ImageDesc},
|
images::{self, Image, ImageDesc},
|
||||||
util::{self, Rgba, WithLifetime},
|
util::{self, Rgba, WithLifetime},
|
||||||
|
@ -489,7 +490,7 @@ impl RenderGraph {
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let pool =
|
let pool =
|
||||||
commands::SingleUseCommandPool::new(device.clone(), device.graphics_queue().clone())?;
|
commands::SingleUseCommandPool::new(device.clone(), device.main_queue().clone())?;
|
||||||
|
|
||||||
let resources = &self.resources;
|
let resources = &self.resources;
|
||||||
let cmds = topo
|
let cmds = topo
|
||||||
|
|
|
@ -7,7 +7,7 @@ pub use crate::egui_pass::{egui_pass, egui_pre_pass};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
buffers::{Buffer, BufferDesc},
|
buffers::{Buffer, BufferDesc},
|
||||||
commands,
|
commands::{self, traits::CommandBufferExt},
|
||||||
device::{Device, DeviceOwned},
|
device::{Device, DeviceOwned},
|
||||||
images::ImageViewDesc,
|
images::ImageViewDesc,
|
||||||
pipeline,
|
pipeline,
|
||||||
|
@ -97,7 +97,7 @@ impl Wireframe {
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let pool = commands::SingleUseCommandPool::new(dev.clone(), dev.graphics_queue().clone())?;
|
let pool = commands::SingleUseCommandPool::new(dev.clone(), dev.main_queue().clone())?;
|
||||||
|
|
||||||
let cmd = pool.alloc()?;
|
let cmd = pool.alloc()?;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue