vkobject_rs/
cmdpool.rs

1
2use crate::prelude::*;
3use std::{
4	fmt::{self, Debug, Formatter},
5	ptr::null,
6	sync::{
7		Arc,
8		atomic::{
9			AtomicBool,
10			AtomicUsize,
11			Ordering,
12		},
13		Mutex,
14		MutexGuard,
15	},
16};
17
18/// The Vulkan command pool, and the associated buffers, fence. Support multiple buffers; you can use one buffer for command recording and another for submitting to a queue, interleaved.
19pub struct VulkanCommandPool {
20	/// The `VulkanDevice` is the associated device
21	pub device: Arc<VulkanDevice>,
22
23	/// The handle to the command pool
24	pub(crate) pool: Mutex<VkCommandPool>,
25
26	/// The command buffers of the command pool
27	pub(crate) cmd_buffers: Vec<VkCommandBuffer>,
28
29	/// The index of the command buffer
30	pub index_of_buffer: AtomicUsize,
31
32	/// The fence for the command pool
33	pub submit_fence: Arc<VulkanFence>,
34}
35
36unsafe impl Send for VulkanCommandPool {}
37unsafe impl Sync for VulkanCommandPool {}
38
39impl VulkanCommandPool {
40	/// Create a new `VulkanCommandPool`
41	pub fn new(device: Arc<VulkanDevice>, num_buffers: usize) -> Result<Self, VulkanError> {
42		let vkcore = device.vkcore.clone();
43		let vk_device = device.get_vk_device();
44		let pool_ci = VkCommandPoolCreateInfo {
45			sType: VkStructureType::VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
46			pNext: null(),
47			queueFamilyIndex: device.get_queue_family_index(),
48			flags:
49				VkCommandPoolCreateFlagBits::VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT as VkCommandPoolCreateFlags |
50				VkCommandPoolCreateFlagBits::VK_COMMAND_POOL_CREATE_TRANSIENT_BIT as VkCommandPoolCreateFlags,
51		};
52		let mut pool: VkCommandPool = null();
53		vkcore.vkCreateCommandPool(vk_device, &pool_ci, null(), &mut pool)?;
54		let cmd_buffers_ci = VkCommandBufferAllocateInfo {
55			sType: VkStructureType::VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
56			pNext: null(),
57			commandPool: pool,
58			level: VkCommandBufferLevel::VK_COMMAND_BUFFER_LEVEL_PRIMARY,
59			commandBufferCount: num_buffers as u32,
60		};
61		let pool = Mutex::new(pool);
62		let mut cmd_buffers: Vec<VkCommandBuffer> = Vec::with_capacity(num_buffers);
63		vkcore.vkAllocateCommandBuffers(vk_device, &cmd_buffers_ci, cmd_buffers.as_mut_ptr())?;
64		unsafe {cmd_buffers.set_len(num_buffers)};
65		let submit_fence = Arc::new(VulkanFence::new(device.clone())?);
66		Ok(Self{
67			device,
68			pool,
69			index_of_buffer: AtomicUsize::new(0),
70			cmd_buffers,
71			submit_fence,
72		})
73	}
74
75	/// Use a command buffer of the command pool to record draw commands
76	pub(crate) fn use_pool<'a>(&'a self, rt_props: Option<Arc<RenderTargetProps>>) -> Result<VulkanCommandPoolInUse<'a>, VulkanError> {
77		let pool_lock = self.pool.lock().unwrap();
78		let begin_info = VkCommandBufferBeginInfo {
79			sType: VkStructureType::VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
80			pNext: null(),
81			flags: 0,
82			pInheritanceInfo: null(),
83		};
84		let buffer_index = self.index_of_buffer.fetch_add(1, Ordering::Relaxed) % self.cmd_buffers.len();
85		self.device.vkcore.vkResetCommandBuffer(self.cmd_buffers[buffer_index], 0)?;
86		self.device.vkcore.vkBeginCommandBuffer(self.cmd_buffers[buffer_index], &begin_info)?;
87		VulkanCommandPoolInUse::new(self, pool_lock, self.cmd_buffers[buffer_index], rt_props)
88	}
89
90	/// Wait for the submit fence to be signaled
91	pub fn wait_for_submit(&self, timeout: u64) -> Result<(), VulkanError> {
92		self.submit_fence.wait(timeout)?;
93		self.submit_fence.unsignal()?;
94		Ok(())
95	}
96}
97
98impl Debug for VulkanCommandPool {
99	fn fmt(&self, f: &mut Formatter) -> fmt::Result {
100		f.debug_struct("VulkanCommandPool")
101		.field("pool", &self.pool)
102		.field("cmd_buffers", &self.cmd_buffers)
103		.field("index_of_buffer", &self.index_of_buffer)
104		.field("submit_fence", &self.submit_fence)
105		.finish()
106	}
107}
108
109impl Drop for VulkanCommandPool {
110	fn drop(&mut self) {
111		let vkcore = self.device.vkcore.clone();
112		proceed_run(self.submit_fence.wait(u64::MAX));
113		proceed_run(self.submit_fence.unsignal());
114		proceed_run(vkcore.vkDestroyCommandPool(self.device.get_vk_device(), *self.pool.lock().unwrap(), null()));
115	}
116}
117
118/// The RAII wrapper for the usage of a Vulkan command pool/buffer. When created, your command could be recorded to the command buffer.
119pub struct VulkanCommandPoolInUse<'a> {
120	/// The `VulkanDevice` is the associated device
121	pub device: Arc<VulkanDevice>,
122
123	/// The command buffer we are using here
124	pub(crate) cmdbuf: VkCommandBuffer,
125
126	/// The command pool to submit commands
127	pub(crate) pool_lock: MutexGuard<'a, VkCommandPool>,
128
129	/// The render target for the command pool to draw to
130	pub rt_props: Option<Arc<RenderTargetProps>>,
131
132	/// The fence indicating if all commands were submitted
133	pub submit_fence: Arc<VulkanFence>,
134
135	/// Is recording commands ended
136	pub(crate) ended: AtomicBool,
137
138	/// Is the commands submitted
139	pub submitted: AtomicBool,
140}
141
142impl<'a> VulkanCommandPoolInUse<'a> {
143	/// Create a RAII binding to the `VulkanCommandPool` in use
144	fn new(cmdpool: &VulkanCommandPool, pool_lock: MutexGuard<'a, VkCommandPool>, cmdbuf: VkCommandBuffer, rt_props: Option<Arc<RenderTargetProps>>) -> Result<Self, VulkanError> {
145		let device = cmdpool.device.clone();
146		let submit_fence = cmdpool.submit_fence.clone();
147		Ok(Self {
148			device,
149			cmdbuf,
150			pool_lock,
151			rt_props,
152			submit_fence,
153			ended: AtomicBool::new(false),
154			submitted: AtomicBool::new(false),
155		})
156	}
157
158	/// Get the current command buffer
159	pub(crate) fn get_vk_cmdbuf(&self) -> VkCommandBuffer {
160		self.cmdbuf
161	}
162
163	/// End recording commands
164	pub fn end_cmd(&self) -> Result<(), VulkanError> {
165		let vkcore = self.device.vkcore.clone();
166		if !self.ended.fetch_or(true, Ordering::AcqRel) {
167			vkcore.vkEndCommandBuffer(self.cmdbuf).inspect_err(|_|{
168				self.ended.store(false, Ordering::Release);
169			})?;
170			Ok(())
171		} else {
172			panic!("Duplicated call to `VulkanCommandPoolInUse::end()`")
173		}
174	}
175
176	/// Submit the commands
177	pub fn submit(&self) -> Result<(), VulkanError> {
178		let vkcore = self.device.vkcore.clone();
179		if !self.ended.load(Ordering::Acquire) {
180			self.end_cmd()?;
181		}
182		if !self.submitted.fetch_or(true, Ordering::AcqRel) {
183			let wait_stage = [VkPipelineStageFlagBits::VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT as VkPipelineStageFlags];
184			let cmd_buffers = [self.cmdbuf];
185
186			let (acquire_semaphores, release_semaphores) = if let Some(rt_props) = &self.rt_props {
187				(
188					vec![rt_props.acquire_semaphore.lock().unwrap().get_vk_semaphore()],
189					vec![rt_props.release_semaphore.get_vk_semaphore()]
190				)
191			} else {
192				(vec![], vec![])
193			};
194			let submit_info = VkSubmitInfo {
195				sType: VkStructureType::VK_STRUCTURE_TYPE_SUBMIT_INFO,
196				pNext: null(),
197				waitSemaphoreCount: acquire_semaphores.len() as u32,
198				pWaitSemaphores: acquire_semaphores.as_ptr(),
199				pWaitDstStageMask: wait_stage.as_ptr(),
200				commandBufferCount: 1,
201				pCommandBuffers: cmd_buffers.as_ptr(),
202				signalSemaphoreCount: release_semaphores.len() as u32,
203				pSignalSemaphores: release_semaphores.as_ptr(),
204			};
205			let submits = [submit_info];
206			self.submit_fence.wait(u64::MAX)?;
207			self.submit_fence.unsignal()?;
208			vkcore.vkQueueSubmit(self.device.get_vk_queue(), submits.len() as u32, submits.as_ptr(), self.submit_fence.get_vk_fence()).inspect_err(|_|{
209				self.submitted.store(false, Ordering::Release);
210			})?;
211			self.submit_fence.set_is_being_signaled();
212			Ok(())
213		} else {
214			panic!("Duplicated call to `VulkanCommandPoolInUse::submit()`, please set the `submitted` member to false to re-submit again if you wish.")
215		}
216	}
217
218	/// End recording to the command buffer and submit the commands to the queue
219	pub fn end(self) {}
220}
221
222impl Debug for VulkanCommandPoolInUse<'_> {
223	fn fmt(&self, f: &mut Formatter) -> fmt::Result {
224		f.debug_struct("VulkanCommandPoolInUse")
225		.field("cmdbuf", &self.cmdbuf)
226		.field("pool_lock", &self.pool_lock)
227		.field("rt_props", &self.rt_props)
228		.field("submit_fence", &self.submit_fence)
229		.field("ended", &self.ended)
230		.field("submitted", &self.submitted)
231		.finish()
232	}
233}
234
235impl Drop for VulkanCommandPoolInUse<'_> {
236	fn drop(&mut self) {
237		if !self.submitted.load(Ordering::Acquire) {
238			proceed_run(self.submit())
239		}
240	}
241}