1pub use crate::pipeline_cache::PipelineCacheValidationError;
2use crate::{
3 binding_model::{CreateBindGroupLayoutError, CreatePipelineLayoutError, PipelineLayout},
4 command::ColorAttachmentError,
5 device::{Device, DeviceError, MissingDownlevelFlags, MissingFeatures, RenderPassContext},
6 id::{PipelineCacheId, PipelineLayoutId, ShaderModuleId},
7 resource::{InvalidResourceError, Labeled, TrackingData},
8 resource_log, validation, Label,
9};
10use arrayvec::ArrayVec;
11use naga::error::ShaderError;
12use std::{borrow::Cow, marker::PhantomData, mem::ManuallyDrop, num::NonZeroU32, sync::Arc};
13use thiserror::Error;
14
15#[derive(Debug)]
19pub(crate) struct LateSizedBufferGroup {
20 pub(crate) shader_sizes: Vec<wgt::BufferAddress>,
22}
23
24#[allow(clippy::large_enum_variant)]
25pub enum ShaderModuleSource<'a> {
26 #[cfg(feature = "wgsl")]
27 Wgsl(Cow<'a, str>),
28 #[cfg(feature = "glsl")]
29 Glsl(Cow<'a, str>, naga::front::glsl::Options),
30 #[cfg(feature = "spirv")]
31 SpirV(Cow<'a, [u32]>, naga::front::spv::Options),
32 Naga(Cow<'static, naga::Module>),
33 #[doc(hidden)]
36 Dummy(PhantomData<&'a ()>),
37}
38
39#[derive(Clone, Debug)]
40#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
41pub struct ShaderModuleDescriptor<'a> {
42 pub label: Label<'a>,
43 #[cfg_attr(feature = "serde", serde(default))]
44 pub runtime_checks: wgt::ShaderRuntimeChecks,
45}
46
47#[derive(Debug)]
48pub struct ShaderModule {
49 pub(crate) raw: ManuallyDrop<Box<dyn hal::DynShaderModule>>,
50 pub(crate) device: Arc<Device>,
51 pub(crate) interface: Option<validation::Interface>,
52 pub(crate) label: String,
54}
55
56impl Drop for ShaderModule {
57 fn drop(&mut self) {
58 resource_log!("Destroy raw {}", self.error_ident());
59 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
61 unsafe {
62 self.device.raw().destroy_shader_module(raw);
63 }
64 }
65}
66
67crate::impl_resource_type!(ShaderModule);
68crate::impl_labeled!(ShaderModule);
69crate::impl_parent_device!(ShaderModule);
70crate::impl_storage_item!(ShaderModule);
71
72impl ShaderModule {
73 pub(crate) fn raw(&self) -> &dyn hal::DynShaderModule {
74 self.raw.as_ref()
75 }
76
77 pub(crate) fn finalize_entry_point_name(
78 &self,
79 stage_bit: wgt::ShaderStages,
80 entry_point: Option<&str>,
81 ) -> Result<String, validation::StageError> {
82 match &self.interface {
83 Some(interface) => interface.finalize_entry_point_name(stage_bit, entry_point),
84 None => entry_point
85 .map(|ep| ep.to_string())
86 .ok_or(validation::StageError::NoEntryPointFound),
87 }
88 }
89}
90
91#[derive(Clone, Debug, Error)]
93#[non_exhaustive]
94pub enum CreateShaderModuleError {
95 #[cfg(any(feature = "wgsl", feature = "indirect-validation"))]
96 #[error(transparent)]
97 Parsing(#[from] ShaderError<naga::front::wgsl::ParseError>),
98 #[cfg(feature = "glsl")]
99 #[error(transparent)]
100 ParsingGlsl(#[from] ShaderError<naga::front::glsl::ParseErrors>),
101 #[cfg(feature = "spirv")]
102 #[error(transparent)]
103 ParsingSpirV(#[from] ShaderError<naga::front::spv::Error>),
104 #[error("Failed to generate the backend-specific code")]
105 Generation,
106 #[error(transparent)]
107 Device(#[from] DeviceError),
108 #[error(transparent)]
109 Validation(#[from] ShaderError<naga::WithSpan<naga::valid::ValidationError>>),
110 #[error(transparent)]
111 MissingFeatures(#[from] MissingFeatures),
112 #[error(
113 "Shader global {bind:?} uses a group index {group} that exceeds the max_bind_groups limit of {limit}."
114 )]
115 InvalidGroupIndex {
116 bind: naga::ResourceBinding,
117 group: u32,
118 limit: u32,
119 },
120}
121
122#[derive(Clone, Debug)]
124#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
125pub struct ProgrammableStageDescriptor<'a> {
126 pub module: ShaderModuleId,
128 pub entry_point: Option<Cow<'a, str>>,
135 pub constants: Cow<'a, naga::back::PipelineConstants>,
143 pub zero_initialize_workgroup_memory: bool,
148}
149
150#[derive(Clone, Debug)]
152pub struct ResolvedProgrammableStageDescriptor<'a> {
153 pub module: Arc<ShaderModule>,
155 pub entry_point: Option<Cow<'a, str>>,
162 pub constants: Cow<'a, naga::back::PipelineConstants>,
170 pub zero_initialize_workgroup_memory: bool,
175}
176
177pub type ImplicitBindGroupCount = u8;
179
180#[derive(Clone, Debug, Error)]
181#[non_exhaustive]
182pub enum ImplicitLayoutError {
183 #[error("The implicit_pipeline_ids arg is required")]
184 MissingImplicitPipelineIds,
185 #[error("Missing IDs for deriving {0} bind groups")]
186 MissingIds(ImplicitBindGroupCount),
187 #[error("Unable to reflect the shader {0:?} interface")]
188 ReflectionError(wgt::ShaderStages),
189 #[error(transparent)]
190 BindGroup(#[from] CreateBindGroupLayoutError),
191 #[error(transparent)]
192 Pipeline(#[from] CreatePipelineLayoutError),
193}
194
195#[derive(Clone, Debug)]
197#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
198pub struct ComputePipelineDescriptor<'a> {
199 pub label: Label<'a>,
200 pub layout: Option<PipelineLayoutId>,
202 pub stage: ProgrammableStageDescriptor<'a>,
204 pub cache: Option<PipelineCacheId>,
206}
207
208#[derive(Clone, Debug)]
210pub struct ResolvedComputePipelineDescriptor<'a> {
211 pub label: Label<'a>,
212 pub layout: Option<Arc<PipelineLayout>>,
214 pub stage: ResolvedProgrammableStageDescriptor<'a>,
216 pub cache: Option<Arc<PipelineCache>>,
218}
219
220#[derive(Clone, Debug, Error)]
221#[non_exhaustive]
222pub enum CreateComputePipelineError {
223 #[error(transparent)]
224 Device(#[from] DeviceError),
225 #[error("Unable to derive an implicit layout")]
226 Implicit(#[from] ImplicitLayoutError),
227 #[error("Error matching shader requirements against the pipeline")]
228 Stage(#[from] validation::StageError),
229 #[error("Internal error: {0}")]
230 Internal(String),
231 #[error("Pipeline constant error: {0}")]
232 PipelineConstants(String),
233 #[error(transparent)]
234 MissingDownlevelFlags(#[from] MissingDownlevelFlags),
235 #[error(transparent)]
236 InvalidResource(#[from] InvalidResourceError),
237}
238
239#[derive(Debug)]
240pub struct ComputePipeline {
241 pub(crate) raw: ManuallyDrop<Box<dyn hal::DynComputePipeline>>,
242 pub(crate) layout: Arc<PipelineLayout>,
243 pub(crate) device: Arc<Device>,
244 pub(crate) _shader_module: Arc<ShaderModule>,
245 pub(crate) late_sized_buffer_groups: ArrayVec<LateSizedBufferGroup, { hal::MAX_BIND_GROUPS }>,
246 pub(crate) label: String,
248 pub(crate) tracking_data: TrackingData,
249}
250
251impl Drop for ComputePipeline {
252 fn drop(&mut self) {
253 resource_log!("Destroy raw {}", self.error_ident());
254 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
256 unsafe {
257 self.device.raw().destroy_compute_pipeline(raw);
258 }
259 }
260}
261
262crate::impl_resource_type!(ComputePipeline);
263crate::impl_labeled!(ComputePipeline);
264crate::impl_parent_device!(ComputePipeline);
265crate::impl_storage_item!(ComputePipeline);
266crate::impl_trackable!(ComputePipeline);
267
268impl ComputePipeline {
269 pub(crate) fn raw(&self) -> &dyn hal::DynComputePipeline {
270 self.raw.as_ref()
271 }
272}
273
274#[derive(Clone, Debug, Error)]
275#[non_exhaustive]
276pub enum CreatePipelineCacheError {
277 #[error(transparent)]
278 Device(#[from] DeviceError),
279 #[error("Pipeline cache validation failed")]
280 Validation(#[from] PipelineCacheValidationError),
281 #[error(transparent)]
282 MissingFeatures(#[from] MissingFeatures),
283}
284
285#[derive(Debug)]
286pub struct PipelineCache {
287 pub(crate) raw: ManuallyDrop<Box<dyn hal::DynPipelineCache>>,
288 pub(crate) device: Arc<Device>,
289 pub(crate) label: String,
291}
292
293impl Drop for PipelineCache {
294 fn drop(&mut self) {
295 resource_log!("Destroy raw {}", self.error_ident());
296 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
298 unsafe {
299 self.device.raw().destroy_pipeline_cache(raw);
300 }
301 }
302}
303
304crate::impl_resource_type!(PipelineCache);
305crate::impl_labeled!(PipelineCache);
306crate::impl_parent_device!(PipelineCache);
307crate::impl_storage_item!(PipelineCache);
308
309impl PipelineCache {
310 pub(crate) fn raw(&self) -> &dyn hal::DynPipelineCache {
311 self.raw.as_ref()
312 }
313}
314
315#[derive(Clone, Debug)]
317#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
318#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
319pub struct VertexBufferLayout<'a> {
320 pub array_stride: wgt::BufferAddress,
322 pub step_mode: wgt::VertexStepMode,
324 pub attributes: Cow<'a, [wgt::VertexAttribute]>,
326}
327
328#[derive(Clone, Debug)]
330#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
331pub struct VertexState<'a> {
332 pub stage: ProgrammableStageDescriptor<'a>,
334 pub buffers: Cow<'a, [VertexBufferLayout<'a>]>,
336}
337
338#[derive(Clone, Debug)]
340pub struct ResolvedVertexState<'a> {
341 pub stage: ResolvedProgrammableStageDescriptor<'a>,
343 pub buffers: Cow<'a, [VertexBufferLayout<'a>]>,
345}
346
347#[derive(Clone, Debug)]
349#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
350pub struct FragmentState<'a> {
351 pub stage: ProgrammableStageDescriptor<'a>,
353 pub targets: Cow<'a, [Option<wgt::ColorTargetState>]>,
355}
356
357#[derive(Clone, Debug)]
359pub struct ResolvedFragmentState<'a> {
360 pub stage: ResolvedProgrammableStageDescriptor<'a>,
362 pub targets: Cow<'a, [Option<wgt::ColorTargetState>]>,
364}
365
366#[derive(Clone, Debug)]
368#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
369pub struct RenderPipelineDescriptor<'a> {
370 pub label: Label<'a>,
371 pub layout: Option<PipelineLayoutId>,
373 pub vertex: VertexState<'a>,
375 #[cfg_attr(feature = "serde", serde(default))]
377 pub primitive: wgt::PrimitiveState,
378 #[cfg_attr(feature = "serde", serde(default))]
380 pub depth_stencil: Option<wgt::DepthStencilState>,
381 #[cfg_attr(feature = "serde", serde(default))]
383 pub multisample: wgt::MultisampleState,
384 pub fragment: Option<FragmentState<'a>>,
386 pub multiview: Option<NonZeroU32>,
389 pub cache: Option<PipelineCacheId>,
391}
392
393#[derive(Clone, Debug)]
395pub struct ResolvedRenderPipelineDescriptor<'a> {
396 pub label: Label<'a>,
397 pub layout: Option<Arc<PipelineLayout>>,
399 pub vertex: ResolvedVertexState<'a>,
401 pub primitive: wgt::PrimitiveState,
403 pub depth_stencil: Option<wgt::DepthStencilState>,
405 pub multisample: wgt::MultisampleState,
407 pub fragment: Option<ResolvedFragmentState<'a>>,
409 pub multiview: Option<NonZeroU32>,
412 pub cache: Option<Arc<PipelineCache>>,
414}
415
416#[derive(Clone, Debug)]
417#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
418pub struct PipelineCacheDescriptor<'a> {
419 pub label: Label<'a>,
420 pub data: Option<Cow<'a, [u8]>>,
421 pub fallback: bool,
422}
423
424#[derive(Clone, Debug, Error)]
425#[non_exhaustive]
426pub enum ColorStateError {
427 #[error("Format {0:?} is not renderable")]
428 FormatNotRenderable(wgt::TextureFormat),
429 #[error("Format {0:?} is not blendable")]
430 FormatNotBlendable(wgt::TextureFormat),
431 #[error("Format {0:?} does not have a color aspect")]
432 FormatNotColor(wgt::TextureFormat),
433 #[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:?}.")]
434 InvalidSampleCount(u32, wgt::TextureFormat, Vec<u32>, Vec<u32>),
435 #[error("Output format {pipeline} is incompatible with the shader {shader}")]
436 IncompatibleFormat {
437 pipeline: validation::NumericType,
438 shader: validation::NumericType,
439 },
440 #[error("Invalid write mask {0:?}")]
441 InvalidWriteMask(wgt::ColorWrites),
442}
443
444#[derive(Clone, Debug, Error)]
445#[non_exhaustive]
446pub enum DepthStencilStateError {
447 #[error("Format {0:?} is not renderable")]
448 FormatNotRenderable(wgt::TextureFormat),
449 #[error("Format {0:?} does not have a depth aspect, but depth test/write is enabled")]
450 FormatNotDepth(wgt::TextureFormat),
451 #[error("Format {0:?} does not have a stencil aspect, but stencil test/write is enabled")]
452 FormatNotStencil(wgt::TextureFormat),
453 #[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:?}.")]
454 InvalidSampleCount(u32, wgt::TextureFormat, Vec<u32>, Vec<u32>),
455}
456
457#[derive(Clone, Debug, Error)]
458#[non_exhaustive]
459pub enum CreateRenderPipelineError {
460 #[error(transparent)]
461 ColorAttachment(#[from] ColorAttachmentError),
462 #[error(transparent)]
463 Device(#[from] DeviceError),
464 #[error("Unable to derive an implicit layout")]
465 Implicit(#[from] ImplicitLayoutError),
466 #[error("Color state [{0}] is invalid")]
467 ColorState(u8, #[source] ColorStateError),
468 #[error("Depth/stencil state is invalid")]
469 DepthStencilState(#[from] DepthStencilStateError),
470 #[error("Invalid sample count {0}")]
471 InvalidSampleCount(u32),
472 #[error("The number of vertex buffers {given} exceeds the limit {limit}")]
473 TooManyVertexBuffers { given: u32, limit: u32 },
474 #[error("The total number of vertex attributes {given} exceeds the limit {limit}")]
475 TooManyVertexAttributes { given: u32, limit: u32 },
476 #[error("Vertex buffer {index} stride {given} exceeds the limit {limit}")]
477 VertexStrideTooLarge { index: u32, given: u32, limit: u32 },
478 #[error("Vertex attribute at location {location} stride {given} exceeds the limit {limit}")]
479 VertexAttributeStrideTooLarge {
480 location: wgt::ShaderLocation,
481 given: u32,
482 limit: u32,
483 },
484 #[error("Vertex buffer {index} stride {stride} does not respect `VERTEX_STRIDE_ALIGNMENT`")]
485 UnalignedVertexStride {
486 index: u32,
487 stride: wgt::BufferAddress,
488 },
489 #[error("Vertex attribute at location {location} has invalid offset {offset}")]
490 InvalidVertexAttributeOffset {
491 location: wgt::ShaderLocation,
492 offset: wgt::BufferAddress,
493 },
494 #[error("Two or more vertex attributes were assigned to the same location in the shader: {0}")]
495 ShaderLocationClash(u32),
496 #[error("Strip index format was not set to None but to {strip_index_format:?} while using the non-strip topology {topology:?}")]
497 StripIndexFormatForNonStripTopology {
498 strip_index_format: Option<wgt::IndexFormat>,
499 topology: wgt::PrimitiveTopology,
500 },
501 #[error("Conservative Rasterization is only supported for wgt::PolygonMode::Fill")]
502 ConservativeRasterizationNonFillPolygonMode,
503 #[error(transparent)]
504 MissingFeatures(#[from] MissingFeatures),
505 #[error(transparent)]
506 MissingDownlevelFlags(#[from] MissingDownlevelFlags),
507 #[error("Error matching {stage:?} shader requirements against the pipeline")]
508 Stage {
509 stage: wgt::ShaderStages,
510 #[source]
511 error: validation::StageError,
512 },
513 #[error("Internal error in {stage:?} shader: {error}")]
514 Internal {
515 stage: wgt::ShaderStages,
516 error: String,
517 },
518 #[error("Pipeline constant error in {stage:?} shader: {error}")]
519 PipelineConstants {
520 stage: wgt::ShaderStages,
521 error: String,
522 },
523 #[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.")]
524 UnalignedShader { group: u32, binding: u32, size: u64 },
525 #[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.")]
526 BlendFactorOnUnsupportedTarget {
527 factor: wgt::BlendFactor,
528 target: u32,
529 },
530 #[error("Pipeline expects the shader entry point to make use of dual-source blending.")]
531 PipelineExpectsShaderToUseDualSourceBlending,
532 #[error("Shader entry point expects the pipeline to make use of dual-source blending.")]
533 ShaderExpectsPipelineToUseDualSourceBlending,
534 #[error("{}", concat!(
535 "At least one color attachment or depth-stencil attachment was expected, ",
536 "but no render target for the pipeline was specified."
537 ))]
538 NoTargetSpecified,
539 #[error(transparent)]
540 InvalidResource(#[from] InvalidResourceError),
541}
542
543bitflags::bitflags! {
544 #[repr(transparent)]
545 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
546 pub struct PipelineFlags: u32 {
547 const BLEND_CONSTANT = 1 << 0;
548 const STENCIL_REFERENCE = 1 << 1;
549 const WRITES_DEPTH = 1 << 2;
550 const WRITES_STENCIL = 1 << 3;
551 }
552}
553
554#[derive(Clone, Copy, Debug)]
556pub struct VertexStep {
557 pub stride: wgt::BufferAddress,
559
560 pub last_stride: wgt::BufferAddress,
562
563 pub mode: wgt::VertexStepMode,
565}
566
567impl Default for VertexStep {
568 fn default() -> Self {
569 Self {
570 stride: 0,
571 last_stride: 0,
572 mode: wgt::VertexStepMode::Vertex,
573 }
574 }
575}
576
577#[derive(Debug)]
578pub struct RenderPipeline {
579 pub(crate) raw: ManuallyDrop<Box<dyn hal::DynRenderPipeline>>,
580 pub(crate) device: Arc<Device>,
581 pub(crate) layout: Arc<PipelineLayout>,
582 pub(crate) _shader_modules: ArrayVec<Arc<ShaderModule>, { hal::MAX_CONCURRENT_SHADER_STAGES }>,
583 pub(crate) pass_context: RenderPassContext,
584 pub(crate) flags: PipelineFlags,
585 pub(crate) strip_index_format: Option<wgt::IndexFormat>,
586 pub(crate) vertex_steps: Vec<VertexStep>,
587 pub(crate) late_sized_buffer_groups: ArrayVec<LateSizedBufferGroup, { hal::MAX_BIND_GROUPS }>,
588 pub(crate) label: String,
590 pub(crate) tracking_data: TrackingData,
591}
592
593impl Drop for RenderPipeline {
594 fn drop(&mut self) {
595 resource_log!("Destroy raw {}", self.error_ident());
596 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
598 unsafe {
599 self.device.raw().destroy_render_pipeline(raw);
600 }
601 }
602}
603
604crate::impl_resource_type!(RenderPipeline);
605crate::impl_labeled!(RenderPipeline);
606crate::impl_parent_device!(RenderPipeline);
607crate::impl_storage_item!(RenderPipeline);
608crate::impl_trackable!(RenderPipeline);
609
610impl RenderPipeline {
611 pub(crate) fn raw(&self) -> &dyn hal::DynRenderPipeline {
612 self.raw.as_ref()
613 }
614}