Skip to main content

vulkano/command_buffer/commands/
secondary.rs

1use crate::{
2    command_buffer::{
3        auto::{RenderPassStateType, Resource, ResourceUseRef2},
4        sys::{CommandBuffer, RecordingCommandBuffer},
5        AutoCommandBufferBuilder, CommandBufferInheritanceRenderPassType, CommandBufferLevel,
6        ResourceInCommand, SecondaryCommandBufferAbstract, SecondaryCommandBufferBufferUsage,
7        SecondaryCommandBufferImageUsage, SecondaryCommandBufferResourcesUsage, SubpassContents,
8    },
9    device::{DeviceOwned, QueueFlags},
10    query::QueryType,
11    Requires, RequiresAllOf, RequiresOneOf, SafeDeref, ValidationError, VulkanObject,
12};
13use smallvec::{smallvec, SmallVec};
14use std::{cmp::min, iter, sync::Arc};
15
16/// # Commands to execute a secondary command buffer inside a primary command buffer.
17///
18/// These commands can be called on any queue that can execute the commands recorded in the
19/// secondary command buffer.
20impl<L> AutoCommandBufferBuilder<L> {
21    /// Executes a secondary command buffer.
22    ///
23    /// If the `flags` that `command_buffer` was created with are more restrictive than those of
24    /// `self`, then `self` will be restricted to match. E.g. executing a secondary command buffer
25    /// with `Flags::OneTimeSubmit` will set `self`'s flags to `Flags::OneTimeSubmit` also.
26    pub fn execute_commands(
27        &mut self,
28        command_buffer: Arc<dyn SecondaryCommandBufferAbstract>,
29    ) -> Result<&mut Self, Box<ValidationError>> {
30        let command_buffer = DropUnlockCommandBuffer::new(command_buffer)?;
31        self.validate_execute_commands(iter::once(&**command_buffer))?;
32
33        Ok(unsafe { self.execute_commands_locked(smallvec![command_buffer]) })
34    }
35
36    /// Executes multiple secondary command buffers in a vector.
37    ///
38    /// This requires that the secondary command buffers do not have resource conflicts; an error
39    /// will be returned if there are any. Use `execute_commands` if you want to ensure that
40    /// resource conflicts are automatically resolved.
41    // TODO ^ would be nice if this just worked without errors
42    pub fn execute_commands_from_vec(
43        &mut self,
44        command_buffers: Vec<Arc<dyn SecondaryCommandBufferAbstract>>,
45    ) -> Result<&mut Self, Box<ValidationError>> {
46        let command_buffers: SmallVec<[_; 4]> = command_buffers
47            .into_iter()
48            .map(DropUnlockCommandBuffer::new)
49            .collect::<Result<_, _>>()?;
50
51        self.validate_execute_commands(command_buffers.iter().map(|cb| &***cb))?;
52
53        Ok(unsafe { self.execute_commands_locked(command_buffers) })
54    }
55
56    fn validate_execute_commands<'a>(
57        &self,
58        command_buffers: impl Iterator<Item = &'a dyn SecondaryCommandBufferAbstract> + Clone,
59    ) -> Result<(), Box<ValidationError>> {
60        self.inner
61            .validate_execute_commands(command_buffers.clone().map(|cb| cb.as_raw()))?;
62
63        if let Some(render_pass_state) = &self.builder_state.render_pass {
64            if render_pass_state.contents != SubpassContents::SecondaryCommandBuffers {
65                return Err(Box::new(ValidationError {
66                    problem: "a render pass instance is active, but its current subpass contents \
67                        is `SubpassContents::SecondaryCommandBuffers`"
68                        .into(),
69                    vuids: &[
70                        "VUID-vkCmdExecuteCommands-contents-06018",
71                        "VUID-vkCmdExecuteCommands-flags-06024",
72                    ],
73                    ..Default::default()
74                }));
75            }
76        }
77
78        if !self.builder_state.queries.is_empty()
79            && !self.device().enabled_features().inherited_queries
80        {
81            return Err(Box::new(ValidationError {
82                problem: "a query is active".into(),
83                requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceFeature(
84                    "inherited_queries",
85                )])]),
86                vuids: &["VUID-vkCmdExecuteCommands-commandBuffer-00101"],
87                ..Default::default()
88            }));
89        }
90
91        for (command_buffer_index, command_buffer) in command_buffers.enumerate() {
92            let inheritance_info = command_buffer.inheritance_info();
93
94            if let Some(render_pass_state) = &self.builder_state.render_pass {
95                let inheritance_render_pass =
96                    inheritance_info.render_pass.as_ref().ok_or_else(|| {
97                        Box::new(ValidationError {
98                            problem: format!(
99                                "a render pass instance is active, but \
100                                `command_buffers[{}].inheritance_info().render_pass` is `None`",
101                                command_buffer_index,
102                            )
103                            .into(),
104                            vuids: &["VUID-vkCmdExecuteCommands-pCommandBuffers-00096"],
105                            ..Default::default()
106                        })
107                    })?;
108
109                match (&render_pass_state.render_pass, inheritance_render_pass) {
110                    (
111                        RenderPassStateType::BeginRenderPass(state),
112                        CommandBufferInheritanceRenderPassType::BeginRenderPass(inheritance_info),
113                    ) => {
114                        if !inheritance_info
115                            .subpass
116                            .render_pass()
117                            .is_compatible_with(state.subpass.render_pass())
118                        {
119                            return Err(Box::new(ValidationError {
120                                context: format!(
121                                    "command_buffers[{}].inheritance_info().render_pass\
122                                    .subpass.render_pass()",
123                                    command_buffer_index
124                                )
125                                .into(),
126                                problem: "is not compatible with the current render pass instance"
127                                    .into(),
128                                vuids: &["VUID-vkCmdExecuteCommands-pBeginInfo-06020"],
129                                ..Default::default()
130                            }));
131                        }
132
133                        if inheritance_info.subpass.index() != state.subpass.index() {
134                            return Err(Box::new(ValidationError {
135                                context: format!(
136                                    "command_buffers[{}].inheritance_info().render_pass\
137                                    .subpass.index()",
138                                    command_buffer_index
139                                )
140                                .into(),
141                                problem: "is not equal to the index of the current \
142                                    subpass instance"
143                                    .into(),
144                                vuids: &["VUID-vkCmdExecuteCommands-pCommandBuffers-06019"],
145                                ..Default::default()
146                            }));
147                        }
148
149                        if let Some(framebuffer) = &inheritance_info.framebuffer {
150                            if framebuffer != state.framebuffer.as_ref().unwrap() {
151                                return Err(Box::new(ValidationError {
152                                    context: format!(
153                                        "command_buffers[{}].inheritance_info().render_pass\
154                                        .framebuffer",
155                                        command_buffer_index
156                                    )
157                                    .into(),
158                                    problem: "is `Some`, but is not equal to the framebuffer of \
159                                        the current render pass instance"
160                                        .into(),
161                                    vuids: &["VUID-vkCmdExecuteCommands-pCommandBuffers-00099"],
162                                    ..Default::default()
163                                }));
164                            }
165                        }
166                    }
167                    (
168                        RenderPassStateType::BeginRendering(_),
169                        CommandBufferInheritanceRenderPassType::BeginRendering(inheritance_info),
170                    ) => {
171                        let attachments = render_pass_state.attachments.as_ref().unwrap();
172
173                        if inheritance_info.color_attachment_formats.len()
174                            != attachments.color_attachments.len()
175                        {
176                            return Err(Box::new(ValidationError {
177                                context: format!(
178                                    "command_buffers[{}].inheritance_info().render_pass\
179                                    .color_attachment_formats.len()",
180                                    command_buffer_index
181                                )
182                                .into(),
183                                problem: "is not equal to the number of color attachments in the \
184                                    current subpass instance"
185                                    .into(),
186                                vuids: &["VUID-vkCmdExecuteCommands-colorAttachmentCount-06027"],
187                                ..Default::default()
188                            }));
189                        }
190
191                        for (color_attachment_index, image_view, inherited_format) in attachments
192                            .color_attachments
193                            .iter()
194                            .zip(inheritance_info.color_attachment_formats.iter().copied())
195                            .enumerate()
196                            .filter_map(|(i, (a, f))| {
197                                a.as_ref().map(|a| (i as u32, &a.image_view, f))
198                            })
199                        {
200                            let required_format = image_view.format();
201
202                            if Some(required_format) != inherited_format {
203                                return Err(Box::new(ValidationError {
204                                    context: format!(
205                                        "command_buffers[{}].inheritance_info().render_pass\
206                                        .color_attachment_formats[{}]",
207                                        command_buffer_index, color_attachment_index
208                                    )
209                                    .into(),
210                                    problem: "is not equal to the format of the \
211                                        corresponding color attachment in the current subpass \
212                                        instance"
213                                        .into(),
214                                    vuids: &["VUID-vkCmdExecuteCommands-imageView-06028"],
215                                    ..Default::default()
216                                }));
217                            }
218
219                            if image_view.image().samples()
220                                != inheritance_info.rasterization_samples
221                            {
222                                return Err(Box::new(ValidationError {
223                                    context: format!(
224                                        "command_buffers[{}].inheritance_info().render_pass\
225                                        .rasterization_samples",
226                                        command_buffer_index,
227                                    )
228                                    .into(),
229                                    problem: "is not equal to the number of samples of the \
230                                        attachments in the current subpass instance"
231                                        .into(),
232                                    vuids: &["VUID-vkCmdExecuteCommands-pNext-06035"],
233                                    ..Default::default()
234                                }));
235                            }
236                        }
237
238                        if let Some((image_view, format)) = attachments
239                            .depth_attachment
240                            .as_ref()
241                            .map(|a| (&a.image_view, inheritance_info.depth_attachment_format))
242                        {
243                            if Some(image_view.format()) != format {
244                                return Err(Box::new(ValidationError {
245                                    context: format!(
246                                        "command_buffers[{}].inheritance_info().render_pass\
247                                        .depth_attachment_format",
248                                        command_buffer_index
249                                    )
250                                    .into(),
251                                    problem: "is not equal to the format of the \
252                                        depth attachment in the current subpass instance"
253                                        .into(),
254                                    vuids: &["VUID-vkCmdExecuteCommands-pDepthAttachment-06029"],
255                                    ..Default::default()
256                                }));
257                            }
258
259                            if image_view.image().samples()
260                                != inheritance_info.rasterization_samples
261                            {
262                                return Err(Box::new(ValidationError {
263                                    context: format!(
264                                        "command_buffers[{}].inheritance_info().render_pass\
265                                        .rasterization_samples",
266                                        command_buffer_index,
267                                    )
268                                    .into(),
269                                    problem: "is not equal to the number of samples of the \
270                                        attachments in the current subpass instance"
271                                        .into(),
272                                    vuids: &["VUID-vkCmdExecuteCommands-pNext-06036"],
273                                    ..Default::default()
274                                }));
275                            }
276                        }
277
278                        if let Some((image_view, format)) = attachments
279                            .stencil_attachment
280                            .as_ref()
281                            .map(|a| (&a.image_view, inheritance_info.stencil_attachment_format))
282                        {
283                            if Some(image_view.format()) != format {
284                                return Err(Box::new(ValidationError {
285                                    context: format!(
286                                        "command_buffers[{}].inheritance_info().render_pass\
287                                        .stencil_attachment_format",
288                                        command_buffer_index
289                                    )
290                                    .into(),
291                                    problem: "is not equal to the format of the \
292                                        stencil attachment in the current subpass instance"
293                                        .into(),
294                                    vuids: &["VUID-vkCmdExecuteCommands-pStencilAttachment-06030"],
295                                    ..Default::default()
296                                }));
297                            }
298
299                            if image_view.image().samples()
300                                != inheritance_info.rasterization_samples
301                            {
302                                return Err(Box::new(ValidationError {
303                                    context: format!(
304                                        "command_buffers[{}].inheritance_info().render_pass\
305                                        .rasterization_samples",
306                                        command_buffer_index,
307                                    )
308                                    .into(),
309                                    problem: "is not equal to the number of samples of the \
310                                        attachments in the current subpass instance"
311                                        .into(),
312                                    vuids: &["VUID-vkCmdExecuteCommands-pNext-06037"],
313                                    ..Default::default()
314                                }));
315                            }
316                        }
317
318                        if inheritance_info.view_mask != render_pass_state.rendering_info.view_mask
319                        {
320                            return Err(Box::new(ValidationError {
321                                context: format!(
322                                    "command_buffers[{}].inheritance_info().render_pass\
323                                    .view_mask",
324                                    command_buffer_index,
325                                )
326                                .into(),
327                                problem: "is not equal to the `view_mask` of the current subpass \
328                                    instance"
329                                    .into(),
330                                vuids: &["VUID-vkCmdExecuteCommands-viewMask-06031"],
331                                ..Default::default()
332                            }));
333                        }
334                    }
335                    (
336                        RenderPassStateType::BeginRenderPass(_),
337                        CommandBufferInheritanceRenderPassType::BeginRendering(_),
338                    ) => {
339                        return Err(Box::new(ValidationError {
340                            context: format!(
341                                "command_buffers[{}].inheritance_info().render_pass",
342                                command_buffer_index
343                            )
344                            .into(),
345                            problem: "is `CommandBufferInheritanceRenderPassType::\
346                                BeginRendering`, but the current render pass instance was begun \
347                                with `begin_render_pass`"
348                                .into(),
349                            // vuids?
350                            ..Default::default()
351                        }));
352                    }
353                    (
354                        RenderPassStateType::BeginRendering(_),
355                        CommandBufferInheritanceRenderPassType::BeginRenderPass(_),
356                    ) => {
357                        return Err(Box::new(ValidationError {
358                            context: format!(
359                                "command_buffers[{}].inheritance_info().render_pass",
360                                command_buffer_index
361                            )
362                            .into(),
363                            problem: "is `CommandBufferInheritanceRenderPassType::\
364                                BeginRenderPass`, but the current render pass instance was begun \
365                                with `begin_rendering`"
366                                .into(),
367                            vuids: &["VUID-vkCmdExecuteCommands-pBeginInfo-06025"],
368                            ..Default::default()
369                        }));
370                    }
371                }
372
373                // TODO:
374                // VUID-vkCmdExecuteCommands-commandBuffer-06533
375                // VUID-vkCmdExecuteCommands-commandBuffer-06534
376                // VUID-vkCmdExecuteCommands-pCommandBuffers-06535
377                // VUID-vkCmdExecuteCommands-pCommandBuffers-06536
378            } else {
379                if inheritance_info.render_pass.is_some() {
380                    return Err(Box::new(ValidationError {
381                        context: format!(
382                            "command_buffers[{}].inheritance_info().render_pass",
383                            command_buffer_index
384                        )
385                        .into(),
386                        problem: "is `Some`, but a render pass instance is not active".into(),
387                        vuids: &["VUID-vkCmdExecuteCommands-pCommandBuffers-00100"],
388                        ..Default::default()
389                    }));
390                }
391            }
392
393            for state in self.builder_state.queries.values() {
394                match state.query_pool.query_type() {
395                    QueryType::Occlusion => {
396                        let inherited_flags =
397                            inheritance_info.occlusion_query.ok_or_else(|| {
398                                Box::new(ValidationError {
399                                    context: format!(
400                                        "command_buffers[{}].inheritance_info().occlusion_query",
401                                        command_buffer_index
402                                    )
403                                    .into(),
404                                    problem:
405                                        "is `None`, but an occlusion query is currently active"
406                                            .into(),
407                                    vuids: &["VUID-vkCmdExecuteCommands-commandBuffer-00102"],
408                                    ..Default::default()
409                                })
410                            })?;
411
412                        if !inherited_flags.contains(state.flags) {
413                            return Err(Box::new(ValidationError {
414                                context: format!(
415                                    "command_buffers[{}].inheritance_info().occlusion_query",
416                                    command_buffer_index
417                                )
418                                .into(),
419                                problem: "is not a superset of the flags of the active \
420                                    occlusion query"
421                                    .into(),
422                                vuids: &["VUID-vkCmdExecuteCommands-commandBuffer-00103"],
423                                ..Default::default()
424                            }));
425                        }
426                    }
427                    QueryType::PipelineStatistics => {
428                        let inherited_flags = inheritance_info.pipeline_statistics;
429
430                        if !inherited_flags.contains(state.query_pool.pipeline_statistics()) {
431                            return Err(Box::new(ValidationError {
432                                context: format!(
433                                    "command_buffers[{}].inheritance_info().pipeline_statistics",
434                                    command_buffer_index
435                                )
436                                .into(),
437                                problem: "is not a superset of the flags of the active \
438                                    `PipelineStatistics` query"
439                                    .into(),
440                                vuids: &["VUID-vkCmdExecuteCommands-commandBuffer-00104"],
441                                ..Default::default()
442                            }));
443                        }
444                    }
445                    _ => (),
446                }
447            }
448        }
449
450        // TODO:
451        // VUID-vkCmdExecuteCommands-pCommandBuffers-00091
452        // VUID-vkCmdExecuteCommands-pCommandBuffers-00092
453        // VUID-vkCmdExecuteCommands-pCommandBuffers-00093
454        // VUID-vkCmdExecuteCommands-pCommandBuffers-00105
455
456        Ok(())
457    }
458
459    #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
460    pub unsafe fn execute_commands_unchecked(
461        &mut self,
462        command_buffers: SmallVec<[Arc<dyn SecondaryCommandBufferAbstract>; 4]>,
463    ) -> &mut Self {
464        unsafe {
465            self.execute_commands_locked(
466                command_buffers
467                    .into_iter()
468                    .map(DropUnlockCommandBuffer::new)
469                    .collect::<Result<_, _>>()
470                    .unwrap(),
471            )
472        }
473    }
474
475    unsafe fn execute_commands_locked(
476        &mut self,
477        command_buffers: SmallVec<[DropUnlockCommandBuffer; 4]>,
478    ) -> &mut Self {
479        // Secondary command buffer could leave the primary in any state.
480        self.builder_state.reset_non_render_pass_states();
481
482        self.add_command(
483            "execute_commands",
484            command_buffers
485                .iter()
486                .enumerate()
487                .flat_map(|(index, command_buffer)| {
488                    let index = index as u32;
489                    let SecondaryCommandBufferResourcesUsage { buffers, images } =
490                        command_buffer.resources_usage();
491
492                    (buffers.iter().map(move |usage| {
493                        let &SecondaryCommandBufferBufferUsage {
494                            use_ref,
495                            ref buffer,
496                            ref range,
497                            memory_access,
498                        } = usage;
499
500                        (
501                            ResourceUseRef2 {
502                                resource_in_command: ResourceInCommand::SecondaryCommandBuffer {
503                                    index,
504                                },
505                                secondary_use_ref: Some(use_ref.into()),
506                            },
507                            Resource::Buffer {
508                                buffer: buffer.clone(),
509                                range: range.clone(),
510                                memory_access,
511                            },
512                        )
513                    }))
514                    .chain(images.iter().map(move |usage| {
515                        let &SecondaryCommandBufferImageUsage {
516                            use_ref,
517                            ref image,
518                            ref subresource_range,
519                            memory_access,
520                            start_layout,
521                            end_layout,
522                        } = usage;
523
524                        (
525                            ResourceUseRef2 {
526                                resource_in_command: ResourceInCommand::SecondaryCommandBuffer {
527                                    index,
528                                },
529                                secondary_use_ref: Some(use_ref.into()),
530                            },
531                            Resource::Image {
532                                image: image.clone(),
533                                subresource_range: subresource_range.clone(),
534                                memory_access,
535                                start_layout,
536                                end_layout,
537                            },
538                        )
539                    }))
540                })
541                .collect(),
542            move |out: &mut RecordingCommandBuffer| {
543                unsafe { out.execute_commands_locked(&command_buffers) };
544            },
545        );
546
547        self
548    }
549}
550
551impl RecordingCommandBuffer {
552    #[inline]
553    pub unsafe fn execute_commands(
554        &mut self,
555        command_buffers: &[&CommandBuffer],
556    ) -> Result<&mut Self, Box<ValidationError>> {
557        self.validate_execute_commands(command_buffers.iter().copied())?;
558
559        Ok(unsafe { self.execute_commands_unchecked(command_buffers) })
560    }
561
562    fn validate_execute_commands<'a>(
563        &self,
564        command_buffers: impl Iterator<Item = &'a CommandBuffer>,
565    ) -> Result<(), Box<ValidationError>> {
566        if self.level() != CommandBufferLevel::Primary {
567            return Err(Box::new(ValidationError {
568                problem: "this command buffer is not a primary command buffer".into(),
569                vuids: &["VUID-vkCmdExecuteCommands-bufferlevel"],
570                ..Default::default()
571            }));
572        }
573
574        if !self
575            .queue_family_properties()
576            .queue_flags
577            .intersects(QueueFlags::TRANSFER | QueueFlags::GRAPHICS | QueueFlags::COMPUTE)
578        {
579            return Err(Box::new(ValidationError {
580                problem: "the queue family of the command buffer does not support \
581                    transfer, graphics or compute operations"
582                    .into(),
583                vuids: &["VUID-vkCmdExecuteCommands-commandBuffer-cmdpool"],
584                ..Default::default()
585            }));
586        }
587
588        for (command_buffer_index, command_buffer) in command_buffers.enumerate() {
589            // VUID-vkCmdExecuteCommands-commonparent
590            assert_eq!(self.device(), command_buffer.device());
591
592            if command_buffer.level() != CommandBufferLevel::Secondary {
593                return Err(Box::new(ValidationError {
594                    context: format!("command_buffers[{}]", command_buffer_index).into(),
595                    problem: "is not a secondary command buffer".into(),
596                    vuids: &["VUID-vkCmdExecuteCommands-pCommandBuffers-00088"],
597                    ..Default::default()
598                }));
599            }
600
601            // TODO:
602            // VUID-vkCmdExecuteCommands-pCommandBuffers-00094
603        }
604
605        // TODO:
606        // VUID-vkCmdExecuteCommands-pCommandBuffers-00091
607        // VUID-vkCmdExecuteCommands-pCommandBuffers-00092
608        // VUID-vkCmdExecuteCommands-pCommandBuffers-00093
609        // VUID-vkCmdExecuteCommands-pCommandBuffers-00105
610
611        // VUID-vkCmdExecuteCommands-pCommandBuffers-00089
612        // Partially ensured by the `RawCommandBuffer` type.
613
614        Ok(())
615    }
616
617    #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
618    pub unsafe fn execute_commands_unchecked(
619        &mut self,
620        command_buffers: &[&CommandBuffer],
621    ) -> &mut Self {
622        if command_buffers.is_empty() {
623            return self;
624        }
625
626        let command_buffers_vk: SmallVec<[_; 4]> =
627            command_buffers.iter().map(|cb| cb.handle()).collect();
628
629        let fns = self.device().fns();
630        unsafe {
631            (fns.v1_0.cmd_execute_commands)(
632                self.handle(),
633                command_buffers_vk.len() as u32,
634                command_buffers_vk.as_ptr(),
635            )
636        };
637
638        // If the secondary is non-concurrent or one-time use, that restricts the primary as
639        // well.
640        self.usage = command_buffers
641            .iter()
642            .map(|cb| cb.usage())
643            .fold(self.usage, min);
644
645        self
646    }
647
648    unsafe fn execute_commands_locked(
649        &mut self,
650        command_buffers: &[DropUnlockCommandBuffer],
651    ) -> &mut Self {
652        if command_buffers.is_empty() {
653            return self;
654        }
655
656        let command_buffers_vk: SmallVec<[_; 4]> =
657            command_buffers.iter().map(|cb| cb.handle()).collect();
658
659        let fns = self.device().fns();
660        unsafe {
661            (fns.v1_0.cmd_execute_commands)(
662                self.handle(),
663                command_buffers_vk.len() as u32,
664                command_buffers_vk.as_ptr(),
665            )
666        };
667
668        // If the secondary is non-concurrent or one-time use, that restricts the primary as
669        // well.
670        self.usage = command_buffers
671            .iter()
672            .map(|cb| cb.usage())
673            .fold(self.usage, min);
674
675        self
676    }
677}
678
679struct DropUnlockCommandBuffer(Arc<dyn SecondaryCommandBufferAbstract>);
680
681impl DropUnlockCommandBuffer {
682    fn new(
683        command_buffer: Arc<dyn SecondaryCommandBufferAbstract>,
684    ) -> Result<Self, Box<ValidationError>> {
685        command_buffer.lock_record()?;
686        Ok(Self(command_buffer))
687    }
688}
689
690impl std::ops::Deref for DropUnlockCommandBuffer {
691    type Target = Arc<dyn SecondaryCommandBufferAbstract>;
692
693    fn deref(&self) -> &Self::Target {
694        &self.0
695    }
696}
697
698unsafe impl SafeDeref for DropUnlockCommandBuffer {}
699
700impl Drop for DropUnlockCommandBuffer {
701    fn drop(&mut self) {
702        unsafe {
703            self.unlock();
704        }
705    }
706}