1use {
4 super::{DriverError, device::Device, fence::Fence},
5 ash::vk::{self, Handle as _},
6 derive_builder::Builder,
7 log::warn,
8 std::{
9 fmt::{Debug, Formatter},
10 slice,
11 thread::panicking,
12 },
13};
14
15#[read_only::cast]
22pub struct CommandBuffer {
23 #[readonly]
27 pub device: Device,
28
29 #[readonly]
33 pub handle: vk::CommandBuffer,
34
35 #[readonly]
37 pub info: CommandBufferInfo,
38
39 pub(crate) pool: vk::CommandPool,
40 release_semaphore: Option<vk::Semaphore>,
41}
42
43impl CommandBuffer {
44 pub fn begin(&self, info: &vk::CommandBufferBeginInfo) -> Result<(), DriverError> {
51 Device::begin_command_buffer(&self.device, self.handle, info)
52 }
53
54 #[profiling::function]
58 pub fn create(
59 device: &Device,
60 info: impl Into<CommandBufferInfo>,
61 ) -> Result<Self, DriverError> {
62 let info = info.into();
63 let device = device.clone();
64
65 let pool = unsafe {
66 device.create_command_pool(
67 &vk::CommandPoolCreateInfo::default()
68 .flags(
69 vk::CommandPoolCreateFlags::TRANSIENT
70 | vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER,
71 )
72 .queue_family_index(info.queue_family_index),
73 None,
74 )
75 }
76 .map_err(|err| {
77 warn!("unable to create command pool: {err}");
78
79 match err {
80 vk::Result::ERROR_OUT_OF_DEVICE_MEMORY | vk::Result::ERROR_OUT_OF_HOST_MEMORY => {
81 DriverError::OutOfMemory
82 }
83 _ => DriverError::Unsupported,
84 }
85 })?;
86
87 let handle = unsafe {
88 device.allocate_command_buffers(
89 &vk::CommandBufferAllocateInfo::default()
90 .command_buffer_count(1)
91 .command_pool(pool)
92 .level(vk::CommandBufferLevel::PRIMARY),
93 )
94 }
95 .map_err(|err| {
96 warn!("unable to allocate command buffer: {err}");
97
98 match err {
99 vk::Result::ERROR_OUT_OF_DEVICE_MEMORY | vk::Result::ERROR_OUT_OF_HOST_MEMORY => {
100 DriverError::OutOfMemory
101 }
102 _ => DriverError::Unsupported,
103 }
104 })?
105 .into_iter()
106 .find(|handle| !handle.is_null())
107 .ok_or_else(|| {
108 warn!("missing command buffer handle");
109
110 DriverError::Unsupported
111 })?;
112
113 Ok(Self {
114 device,
115 handle,
116 info,
117 pool,
118 release_semaphore: None,
119 })
120 }
121
122 pub fn end(&self) -> Result<(), DriverError> {
129 Device::end_command_buffer(&self.device, self.handle)
130 }
131
132 pub fn end_render_pass(&self) {
134 unsafe {
135 self.device.cmd_end_render_pass(self.handle);
136 }
137 }
138
139 pub fn queue_submit(
156 &self,
157 queue: vk::Queue,
158 fence: &mut Fence,
159 submits: &[vk::SubmitInfo],
160 ) -> Result<(), DriverError> {
161 Device::queue_submit(&self.device, queue, submits, fence.handle)?;
162 fence.mark_queued();
163
164 Ok(())
165 }
166
167 pub fn queue_submit2(
173 &self,
174 queue: vk::Queue,
175 fence: &mut Fence,
176 submits: &[vk::SubmitInfo2],
177 ) -> Result<(), DriverError> {
178 Device::queue_submit2(&self.device, queue, submits, fence.handle)?;
179 fence.mark_queued();
180
181 Ok(())
182 }
183
184 pub(crate) fn release_semaphore(&mut self) -> Result<vk::Semaphore, DriverError> {
189 if let Some(semaphore) = self.release_semaphore {
190 return Ok(semaphore);
191 }
192
193 let semaphore = Device::create_semaphore(&self.device)?;
194
195 Device::try_set_debug_utils_object_name(&self.device, semaphore, "queue ownership release");
196
197 self.release_semaphore = Some(semaphore);
198
199 Ok(semaphore)
200 }
201
202 pub fn set_debug_name(&self, name: impl AsRef<str>) {
204 Device::try_set_debug_utils_object_name(&self.device, self.handle, &name);
205 Device::try_set_private_data_object_name(
206 &self.device,
207 vk::ObjectType::COMMAND_BUFFER,
208 self.handle,
209 &name,
210 );
211 }
212
213 pub fn with_debug_name(self, name: impl AsRef<str>) -> Self {
215 self.set_debug_name(name);
216
217 self
218 }
219}
220
221impl AsRef<CommandBuffer> for CommandBuffer {
222 fn as_ref(&self) -> &CommandBuffer {
223 self
224 }
225}
226
227impl Debug for CommandBuffer {
228 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
229 let mut res = f.debug_struct(stringify!(CommandBuffer));
230
231 if let Some(debug_name) = &Device::private_data_object_name(
232 &self.device,
233 vk::ObjectType::COMMAND_BUFFER,
234 self.handle,
235 ) {
236 res.field("debug_name", debug_name);
237 }
238
239 res.field("handle", &self.handle).finish_non_exhaustive()
240 }
241}
242
243impl Drop for CommandBuffer {
244 #[profiling::function]
245 fn drop(&mut self) {
246 if panicking() {
247 return;
248 }
249
250 Device::try_clear_private_data_object_name(
251 &self.device,
252 vk::ObjectType::COMMAND_BUFFER,
253 self.handle,
254 );
255
256 unsafe {
257 if let Some(semaphore) = self.release_semaphore.take() {
258 self.device.destroy_semaphore(semaphore, None);
259 }
260
261 self.device
262 .free_command_buffers(self.pool, slice::from_ref(&self.handle));
263 self.device.destroy_command_pool(self.pool, None);
264 }
265 }
266}
267
268#[derive(Builder, Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
270#[builder(
271 build_fn(private, name = "fallible_build"),
272 derive(Clone, Copy, Debug),
273 pattern = "owned"
274)]
275pub struct CommandBufferInfo {
276 #[builder(default)]
280 pub queue_family_index: u32,
281}
282
283impl CommandBufferInfo {
284 pub fn new(queue_family_index: u32) -> Self {
286 Self { queue_family_index }
287 }
288}
289
290impl CommandBufferInfo {
291 pub fn builder() -> CommandBufferInfoBuilder {
293 Default::default()
294 }
295
296 pub fn into_builder(self) -> CommandBufferInfoBuilder {
298 CommandBufferInfoBuilder {
299 queue_family_index: Some(self.queue_family_index),
300 }
301 }
302}
303
304impl From<CommandBufferInfoBuilder> for CommandBufferInfo {
305 fn from(info: CommandBufferInfoBuilder) -> Self {
306 info.build()
307 }
308}
309
310impl CommandBufferInfoBuilder {
311 #[inline(always)]
313 pub fn build(self) -> CommandBufferInfo {
314 self.fallible_build().expect("invalid command buffer info")
315 }
316}
317
318#[cfg(test)]
319mod test {
320 use super::*;
321
322 type Info = CommandBufferInfo;
323 type Builder = CommandBufferInfoBuilder;
324
325 #[test]
326 pub fn command_buffer_info() {
327 let info = Info::new(3);
328 let builder = info.into_builder().build();
329
330 assert_eq!(info, builder);
331 }
332
333 #[test]
334 pub fn command_buffer_info_builder_default_queue_family_index() {
335 assert_eq!(Builder::default().build(), Info::new(0));
336 }
337}