wgpu_core/
pipeline.rs

1use alloc::{
2    borrow::Cow,
3    boxed::Box,
4    string::{String, ToString as _},
5    sync::Arc,
6    vec::Vec,
7};
8use core::{marker::PhantomData, mem::ManuallyDrop, num::NonZeroU32};
9
10use arrayvec::ArrayVec;
11use naga::error::ShaderError;
12use thiserror::Error;
13
14pub use crate::pipeline_cache::PipelineCacheValidationError;
15use crate::{
16    binding_model::{CreateBindGroupLayoutError, CreatePipelineLayoutError, PipelineLayout},
17    command::ColorAttachmentError,
18    device::{Device, DeviceError, MissingDownlevelFlags, MissingFeatures, RenderPassContext},
19    id::{PipelineCacheId, PipelineLayoutId, ShaderModuleId},
20    resource::{InvalidResourceError, Labeled, TrackingData},
21    resource_log, validation, Label,
22};
23
24/// Information about buffer bindings, which
25/// is validated against the shader (and pipeline)
26/// at draw time as opposed to initialization time.
27#[derive(Debug)]
28pub(crate) struct LateSizedBufferGroup {
29    // The order has to match `BindGroup::late_buffer_binding_sizes`.
30    pub(crate) shader_sizes: Vec<wgt::BufferAddress>,
31}
32
33#[allow(clippy::large_enum_variant)]
34pub enum ShaderModuleSource<'a> {
35    #[cfg(feature = "wgsl")]
36    Wgsl(Cow<'a, str>),
37    #[cfg(feature = "glsl")]
38    Glsl(Cow<'a, str>, naga::front::glsl::Options),
39    #[cfg(feature = "spirv")]
40    SpirV(Cow<'a, [u32]>, naga::front::spv::Options),
41    Naga(Cow<'static, naga::Module>),
42    /// Dummy variant because `Naga` doesn't have a lifetime and without enough active features it
43    /// could be the last one active.
44    #[doc(hidden)]
45    Dummy(PhantomData<&'a ()>),
46}
47
48#[derive(Clone, Debug)]
49#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
50pub struct ShaderModuleDescriptor<'a> {
51    pub label: Label<'a>,
52    #[cfg_attr(feature = "serde", serde(default))]
53    pub runtime_checks: wgt::ShaderRuntimeChecks,
54}
55
56pub type ShaderModuleDescriptorPassthrough<'a> =
57    wgt::CreateShaderModuleDescriptorPassthrough<'a, Label<'a>>;
58
59#[derive(Debug)]
60pub struct ShaderModule {
61    pub(crate) raw: ManuallyDrop<Box<dyn hal::DynShaderModule>>,
62    pub(crate) device: Arc<Device>,
63    pub(crate) interface: Option<validation::Interface>,
64    /// The `label` from the descriptor used to create the resource.
65    pub(crate) label: String,
66}
67
68impl Drop for ShaderModule {
69    fn drop(&mut self) {
70        resource_log!("Destroy raw {}", self.error_ident());
71        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.
72        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
73        unsafe {
74            self.device.raw().destroy_shader_module(raw);
75        }
76    }
77}
78
79crate::impl_resource_type!(ShaderModule);
80crate::impl_labeled!(ShaderModule);
81crate::impl_parent_device!(ShaderModule);
82crate::impl_storage_item!(ShaderModule);
83
84impl ShaderModule {
85    pub(crate) fn raw(&self) -> &dyn hal::DynShaderModule {
86        self.raw.as_ref()
87    }
88
89    pub(crate) fn finalize_entry_point_name(
90        &self,
91        stage_bit: wgt::ShaderStages,
92        entry_point: Option<&str>,
93    ) -> Result<String, validation::StageError> {
94        match &self.interface {
95            Some(interface) => interface.finalize_entry_point_name(stage_bit, entry_point),
96            None => entry_point
97                .map(|ep| ep.to_string())
98                .ok_or(validation::StageError::NoEntryPointFound),
99        }
100    }
101}
102
103//Note: `Clone` would require `WithSpan: Clone`.
104#[derive(Clone, Debug, Error)]
105#[non_exhaustive]
106pub enum CreateShaderModuleError {
107    #[cfg(feature = "wgsl")]
108    #[error(transparent)]
109    Parsing(#[from] ShaderError<naga::front::wgsl::ParseError>),
110    #[cfg(feature = "glsl")]
111    #[error(transparent)]
112    ParsingGlsl(#[from] ShaderError<naga::front::glsl::ParseErrors>),
113    #[cfg(feature = "spirv")]
114    #[error(transparent)]
115    ParsingSpirV(#[from] ShaderError<naga::front::spv::Error>),
116    #[error("Failed to generate the backend-specific code")]
117    Generation,
118    #[error(transparent)]
119    Device(#[from] DeviceError),
120    #[error(transparent)]
121    Validation(#[from] ShaderError<naga::WithSpan<naga::valid::ValidationError>>),
122    #[error(transparent)]
123    MissingFeatures(#[from] MissingFeatures),
124    #[error(
125        "Shader global {bind:?} uses a group index {group} that exceeds the max_bind_groups limit of {limit}."
126    )]
127    InvalidGroupIndex {
128        bind: naga::ResourceBinding,
129        group: u32,
130        limit: u32,
131    },
132}
133
134/// Describes a programmable pipeline stage.
135#[derive(Clone, Debug)]
136#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
137pub struct ProgrammableStageDescriptor<'a, SM = ShaderModuleId> {
138    /// The compiled shader module for this stage.
139    pub module: SM,
140    /// The name of the entry point in the compiled shader. The name is selected using the
141    /// following logic:
142    ///
143    /// * If `Some(name)` is specified, there must be a function with this name in the shader.
144    /// * If a single entry point associated with this stage must be in the shader, then proceed as
145    ///   if `Some(…)` was specified with that entry point's name.
146    pub entry_point: Option<Cow<'a, str>>,
147    /// Specifies the values of pipeline-overridable constants in the shader module.
148    ///
149    /// If an `@id` attribute was specified on the declaration,
150    /// the key must be the pipeline constant ID as a decimal ASCII number; if not,
151    /// the key must be the constant's identifier name.
152    ///
153    /// The value may represent any of WGSL's concrete scalar types.
154    pub constants: naga::back::PipelineConstants,
155    /// Whether workgroup scoped memory will be initialized with zero values for this stage.
156    ///
157    /// This is required by the WebGPU spec, but may have overhead which can be avoided
158    /// for cross-platform applications
159    pub zero_initialize_workgroup_memory: bool,
160}
161
162/// cbindgen:ignore
163pub type ResolvedProgrammableStageDescriptor<'a> =
164    ProgrammableStageDescriptor<'a, Arc<ShaderModule>>;
165
166/// Number of implicit bind groups derived at pipeline creation.
167pub type ImplicitBindGroupCount = u8;
168
169#[derive(Clone, Debug, Error)]
170#[non_exhaustive]
171pub enum ImplicitLayoutError {
172    #[error("The implicit_pipeline_ids arg is required")]
173    MissingImplicitPipelineIds,
174    #[error("Missing IDs for deriving {0} bind groups")]
175    MissingIds(ImplicitBindGroupCount),
176    #[error("Unable to reflect the shader {0:?} interface")]
177    ReflectionError(wgt::ShaderStages),
178    #[error(transparent)]
179    BindGroup(#[from] CreateBindGroupLayoutError),
180    #[error(transparent)]
181    Pipeline(#[from] CreatePipelineLayoutError),
182}
183
184/// Describes a compute pipeline.
185#[derive(Clone, Debug)]
186#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
187pub struct ComputePipelineDescriptor<
188    'a,
189    PLL = PipelineLayoutId,
190    SM = ShaderModuleId,
191    PLC = PipelineCacheId,
192> {
193    pub label: Label<'a>,
194    /// The layout of bind groups for this pipeline.
195    pub layout: Option<PLL>,
196    /// The compiled compute stage and its entry point.
197    pub stage: ProgrammableStageDescriptor<'a, SM>,
198    /// The pipeline cache to use when creating this pipeline.
199    pub cache: Option<PLC>,
200}
201
202/// cbindgen:ignore
203pub type ResolvedComputePipelineDescriptor<'a> =
204    ComputePipelineDescriptor<'a, Arc<PipelineLayout>, Arc<ShaderModule>, Arc<PipelineCache>>;
205
206#[derive(Clone, Debug, Error)]
207#[non_exhaustive]
208pub enum CreateComputePipelineError {
209    #[error(transparent)]
210    Device(#[from] DeviceError),
211    #[error("Unable to derive an implicit layout")]
212    Implicit(#[from] ImplicitLayoutError),
213    #[error("Error matching shader requirements against the pipeline")]
214    Stage(#[from] validation::StageError),
215    #[error("Internal error: {0}")]
216    Internal(String),
217    #[error("Pipeline constant error: {0}")]
218    PipelineConstants(String),
219    #[error(transparent)]
220    MissingDownlevelFlags(#[from] MissingDownlevelFlags),
221    #[error(transparent)]
222    InvalidResource(#[from] InvalidResourceError),
223}
224
225#[derive(Debug)]
226pub struct ComputePipeline {
227    pub(crate) raw: ManuallyDrop<Box<dyn hal::DynComputePipeline>>,
228    pub(crate) layout: Arc<PipelineLayout>,
229    pub(crate) device: Arc<Device>,
230    pub(crate) _shader_module: Arc<ShaderModule>,
231    pub(crate) late_sized_buffer_groups: ArrayVec<LateSizedBufferGroup, { hal::MAX_BIND_GROUPS }>,
232    /// The `label` from the descriptor used to create the resource.
233    pub(crate) label: String,
234    pub(crate) tracking_data: TrackingData,
235}
236
237impl Drop for ComputePipeline {
238    fn drop(&mut self) {
239        resource_log!("Destroy raw {}", self.error_ident());
240        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.
241        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
242        unsafe {
243            self.device.raw().destroy_compute_pipeline(raw);
244        }
245    }
246}
247
248crate::impl_resource_type!(ComputePipeline);
249crate::impl_labeled!(ComputePipeline);
250crate::impl_parent_device!(ComputePipeline);
251crate::impl_storage_item!(ComputePipeline);
252crate::impl_trackable!(ComputePipeline);
253
254impl ComputePipeline {
255    pub(crate) fn raw(&self) -> &dyn hal::DynComputePipeline {
256        self.raw.as_ref()
257    }
258}
259
260#[derive(Clone, Debug, Error)]
261#[non_exhaustive]
262pub enum CreatePipelineCacheError {
263    #[error(transparent)]
264    Device(#[from] DeviceError),
265    #[error("Pipeline cache validation failed")]
266    Validation(#[from] PipelineCacheValidationError),
267    #[error(transparent)]
268    MissingFeatures(#[from] MissingFeatures),
269}
270
271#[derive(Debug)]
272pub struct PipelineCache {
273    pub(crate) raw: ManuallyDrop<Box<dyn hal::DynPipelineCache>>,
274    pub(crate) device: Arc<Device>,
275    /// The `label` from the descriptor used to create the resource.
276    pub(crate) label: String,
277}
278
279impl Drop for PipelineCache {
280    fn drop(&mut self) {
281        resource_log!("Destroy raw {}", self.error_ident());
282        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.
283        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
284        unsafe {
285            self.device.raw().destroy_pipeline_cache(raw);
286        }
287    }
288}
289
290crate::impl_resource_type!(PipelineCache);
291crate::impl_labeled!(PipelineCache);
292crate::impl_parent_device!(PipelineCache);
293crate::impl_storage_item!(PipelineCache);
294
295impl PipelineCache {
296    pub(crate) fn raw(&self) -> &dyn hal::DynPipelineCache {
297        self.raw.as_ref()
298    }
299}
300
301/// Describes how the vertex buffer is interpreted.
302#[derive(Clone, Debug)]
303#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
304#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
305pub struct VertexBufferLayout<'a> {
306    /// The stride, in bytes, between elements of this buffer.
307    pub array_stride: wgt::BufferAddress,
308    /// How often this vertex buffer is "stepped" forward.
309    pub step_mode: wgt::VertexStepMode,
310    /// The list of attributes which comprise a single vertex.
311    pub attributes: Cow<'a, [wgt::VertexAttribute]>,
312}
313
314/// Describes the vertex process in a render pipeline.
315#[derive(Clone, Debug)]
316#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
317pub struct VertexState<'a, SM = ShaderModuleId> {
318    /// The compiled vertex stage and its entry point.
319    pub stage: ProgrammableStageDescriptor<'a, SM>,
320    /// The format of any vertex buffers used with this pipeline.
321    pub buffers: Cow<'a, [VertexBufferLayout<'a>]>,
322}
323
324/// cbindgen:ignore
325pub type ResolvedVertexState<'a> = VertexState<'a, Arc<ShaderModule>>;
326
327/// Describes fragment processing in a render pipeline.
328#[derive(Clone, Debug)]
329#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
330pub struct FragmentState<'a, SM = ShaderModuleId> {
331    /// The compiled fragment stage and its entry point.
332    pub stage: ProgrammableStageDescriptor<'a, SM>,
333    /// The effect of draw calls on the color aspect of the output target.
334    pub targets: Cow<'a, [Option<wgt::ColorTargetState>]>,
335}
336
337/// cbindgen:ignore
338pub type ResolvedFragmentState<'a> = FragmentState<'a, Arc<ShaderModule>>;
339
340/// Describes a render (graphics) pipeline.
341#[derive(Clone, Debug)]
342#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
343pub struct RenderPipelineDescriptor<
344    'a,
345    PLL = PipelineLayoutId,
346    SM = ShaderModuleId,
347    PLC = PipelineCacheId,
348> {
349    pub label: Label<'a>,
350    /// The layout of bind groups for this pipeline.
351    pub layout: Option<PLL>,
352    /// The vertex processing state for this pipeline.
353    pub vertex: VertexState<'a, SM>,
354    /// The properties of the pipeline at the primitive assembly and rasterization level.
355    #[cfg_attr(feature = "serde", serde(default))]
356    pub primitive: wgt::PrimitiveState,
357    /// The effect of draw calls on the depth and stencil aspects of the output target, if any.
358    #[cfg_attr(feature = "serde", serde(default))]
359    pub depth_stencil: Option<wgt::DepthStencilState>,
360    /// The multi-sampling properties of the pipeline.
361    #[cfg_attr(feature = "serde", serde(default))]
362    pub multisample: wgt::MultisampleState,
363    /// The fragment processing state for this pipeline.
364    pub fragment: Option<FragmentState<'a, SM>>,
365    /// If the pipeline will be used with a multiview render pass, this indicates how many array
366    /// layers the attachments will have.
367    pub multiview: Option<NonZeroU32>,
368    /// The pipeline cache to use when creating this pipeline.
369    pub cache: Option<PLC>,
370}
371
372/// cbindgen:ignore
373pub type ResolvedRenderPipelineDescriptor<'a> =
374    RenderPipelineDescriptor<'a, Arc<PipelineLayout>, Arc<ShaderModule>, Arc<PipelineCache>>;
375
376#[derive(Clone, Debug)]
377#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
378pub struct PipelineCacheDescriptor<'a> {
379    pub label: Label<'a>,
380    pub data: Option<Cow<'a, [u8]>>,
381    pub fallback: bool,
382}
383
384#[derive(Clone, Debug, Error)]
385#[non_exhaustive]
386pub enum ColorStateError {
387    #[error("Format {0:?} is not renderable")]
388    FormatNotRenderable(wgt::TextureFormat),
389    #[error("Format {0:?} is not blendable")]
390    FormatNotBlendable(wgt::TextureFormat),
391    #[error("Format {0:?} does not have a color aspect")]
392    FormatNotColor(wgt::TextureFormat),
393    #[error("Sample count {0} is not supported by format {1:?} on this device. The WebGPU spec guarantees {2:?} samples are supported by this format. With the TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES feature your device supports {3:?}.")]
394    InvalidSampleCount(u32, wgt::TextureFormat, Vec<u32>, Vec<u32>),
395    #[error("Output format {pipeline} is incompatible with the shader {shader}")]
396    IncompatibleFormat {
397        pipeline: validation::NumericType,
398        shader: validation::NumericType,
399    },
400    #[error("Invalid write mask {0:?}")]
401    InvalidWriteMask(wgt::ColorWrites),
402}
403
404#[derive(Clone, Debug, Error)]
405#[non_exhaustive]
406pub enum DepthStencilStateError {
407    #[error("Format {0:?} is not renderable")]
408    FormatNotRenderable(wgt::TextureFormat),
409    #[error("Format {0:?} does not have a depth aspect, but depth test/write is enabled")]
410    FormatNotDepth(wgt::TextureFormat),
411    #[error("Format {0:?} does not have a stencil aspect, but stencil test/write is enabled")]
412    FormatNotStencil(wgt::TextureFormat),
413    #[error("Sample count {0} is not supported by format {1:?} on this device. The WebGPU spec guarantees {2:?} samples are supported by this format. With the TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES feature your device supports {3:?}.")]
414    InvalidSampleCount(u32, wgt::TextureFormat, Vec<u32>, Vec<u32>),
415}
416
417#[derive(Clone, Debug, Error)]
418#[non_exhaustive]
419pub enum CreateRenderPipelineError {
420    #[error(transparent)]
421    ColorAttachment(#[from] ColorAttachmentError),
422    #[error(transparent)]
423    Device(#[from] DeviceError),
424    #[error("Unable to derive an implicit layout")]
425    Implicit(#[from] ImplicitLayoutError),
426    #[error("Color state [{0}] is invalid")]
427    ColorState(u8, #[source] ColorStateError),
428    #[error("Depth/stencil state is invalid")]
429    DepthStencilState(#[from] DepthStencilStateError),
430    #[error("Invalid sample count {0}")]
431    InvalidSampleCount(u32),
432    #[error("The number of vertex buffers {given} exceeds the limit {limit}")]
433    TooManyVertexBuffers { given: u32, limit: u32 },
434    #[error("The total number of vertex attributes {given} exceeds the limit {limit}")]
435    TooManyVertexAttributes { given: u32, limit: u32 },
436    #[error("Vertex buffer {index} stride {given} exceeds the limit {limit}")]
437    VertexStrideTooLarge { index: u32, given: u32, limit: u32 },
438    #[error("Vertex attribute at location {location} stride {given} exceeds the limit {limit}")]
439    VertexAttributeStrideTooLarge {
440        location: wgt::ShaderLocation,
441        given: u32,
442        limit: u32,
443    },
444    #[error("Vertex buffer {index} stride {stride} does not respect `VERTEX_STRIDE_ALIGNMENT`")]
445    UnalignedVertexStride {
446        index: u32,
447        stride: wgt::BufferAddress,
448    },
449    #[error("Vertex attribute at location {location} has invalid offset {offset}")]
450    InvalidVertexAttributeOffset {
451        location: wgt::ShaderLocation,
452        offset: wgt::BufferAddress,
453    },
454    #[error("Two or more vertex attributes were assigned to the same location in the shader: {0}")]
455    ShaderLocationClash(u32),
456    #[error("Strip index format was not set to None but to {strip_index_format:?} while using the non-strip topology {topology:?}")]
457    StripIndexFormatForNonStripTopology {
458        strip_index_format: Option<wgt::IndexFormat>,
459        topology: wgt::PrimitiveTopology,
460    },
461    #[error("Conservative Rasterization is only supported for wgt::PolygonMode::Fill")]
462    ConservativeRasterizationNonFillPolygonMode,
463    #[error(transparent)]
464    MissingFeatures(#[from] MissingFeatures),
465    #[error(transparent)]
466    MissingDownlevelFlags(#[from] MissingDownlevelFlags),
467    #[error("Error matching {stage:?} shader requirements against the pipeline")]
468    Stage {
469        stage: wgt::ShaderStages,
470        #[source]
471        error: validation::StageError,
472    },
473    #[error("Internal error in {stage:?} shader: {error}")]
474    Internal {
475        stage: wgt::ShaderStages,
476        error: String,
477    },
478    #[error("Pipeline constant error in {stage:?} shader: {error}")]
479    PipelineConstants {
480        stage: wgt::ShaderStages,
481        error: String,
482    },
483    #[error("In the provided shader, the type given for group {group} binding {binding} has a size of {size}. As the device does not support `DownlevelFlags::BUFFER_BINDINGS_NOT_16_BYTE_ALIGNED`, the type must have a size that is a multiple of 16 bytes.")]
484    UnalignedShader { group: u32, binding: u32, size: u64 },
485    #[error("Using the blend factor {factor:?} for render target {target} is not possible. Only the first render target may be used when dual-source blending.")]
486    BlendFactorOnUnsupportedTarget {
487        factor: wgt::BlendFactor,
488        target: u32,
489    },
490    #[error("Pipeline expects the shader entry point to make use of dual-source blending.")]
491    PipelineExpectsShaderToUseDualSourceBlending,
492    #[error("Shader entry point expects the pipeline to make use of dual-source blending.")]
493    ShaderExpectsPipelineToUseDualSourceBlending,
494    #[error("{}", concat!(
495        "At least one color attachment or depth-stencil attachment was expected, ",
496        "but no render target for the pipeline was specified."
497    ))]
498    NoTargetSpecified,
499    #[error(transparent)]
500    InvalidResource(#[from] InvalidResourceError),
501}
502
503bitflags::bitflags! {
504    #[repr(transparent)]
505    #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
506    pub struct PipelineFlags: u32 {
507        const BLEND_CONSTANT = 1 << 0;
508        const STENCIL_REFERENCE = 1 << 1;
509        const WRITES_DEPTH = 1 << 2;
510        const WRITES_STENCIL = 1 << 3;
511    }
512}
513
514/// How a render pipeline will retrieve attributes from a particular vertex buffer.
515#[derive(Clone, Copy, Debug)]
516pub struct VertexStep {
517    /// The byte stride in the buffer between one attribute value and the next.
518    pub stride: wgt::BufferAddress,
519
520    /// The byte size required to fit the last vertex in the stream.
521    pub last_stride: wgt::BufferAddress,
522
523    /// Whether the buffer is indexed by vertex number or instance number.
524    pub mode: wgt::VertexStepMode,
525}
526
527impl Default for VertexStep {
528    fn default() -> Self {
529        Self {
530            stride: 0,
531            last_stride: 0,
532            mode: wgt::VertexStepMode::Vertex,
533        }
534    }
535}
536
537#[derive(Debug)]
538pub struct RenderPipeline {
539    pub(crate) raw: ManuallyDrop<Box<dyn hal::DynRenderPipeline>>,
540    pub(crate) device: Arc<Device>,
541    pub(crate) layout: Arc<PipelineLayout>,
542    pub(crate) _shader_modules: ArrayVec<Arc<ShaderModule>, { hal::MAX_CONCURRENT_SHADER_STAGES }>,
543    pub(crate) pass_context: RenderPassContext,
544    pub(crate) flags: PipelineFlags,
545    pub(crate) strip_index_format: Option<wgt::IndexFormat>,
546    pub(crate) vertex_steps: Vec<VertexStep>,
547    pub(crate) late_sized_buffer_groups: ArrayVec<LateSizedBufferGroup, { hal::MAX_BIND_GROUPS }>,
548    /// The `label` from the descriptor used to create the resource.
549    pub(crate) label: String,
550    pub(crate) tracking_data: TrackingData,
551}
552
553impl Drop for RenderPipeline {
554    fn drop(&mut self) {
555        resource_log!("Destroy raw {}", self.error_ident());
556        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.
557        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
558        unsafe {
559            self.device.raw().destroy_render_pipeline(raw);
560        }
561    }
562}
563
564crate::impl_resource_type!(RenderPipeline);
565crate::impl_labeled!(RenderPipeline);
566crate::impl_parent_device!(RenderPipeline);
567crate::impl_storage_item!(RenderPipeline);
568crate::impl_trackable!(RenderPipeline);
569
570impl RenderPipeline {
571    pub(crate) fn raw(&self) -> &dyn hal::DynRenderPipeline {
572        self.raw.as_ref()
573    }
574}