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 shader_bound_checks: wgt::ShaderBoundChecks,
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 #[error("Internal error: {0}")]
284 Internal(String),
285}
286
287#[derive(Debug)]
288pub struct PipelineCache {
289 pub(crate) raw: ManuallyDrop<Box<dyn hal::DynPipelineCache>>,
290 pub(crate) device: Arc<Device>,
291 pub(crate) label: String,
293}
294
295impl Drop for PipelineCache {
296 fn drop(&mut self) {
297 resource_log!("Destroy raw {}", self.error_ident());
298 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
300 unsafe {
301 self.device.raw().destroy_pipeline_cache(raw);
302 }
303 }
304}
305
306crate::impl_resource_type!(PipelineCache);
307crate::impl_labeled!(PipelineCache);
308crate::impl_parent_device!(PipelineCache);
309crate::impl_storage_item!(PipelineCache);
310
311impl PipelineCache {
312 pub(crate) fn raw(&self) -> &dyn hal::DynPipelineCache {
313 self.raw.as_ref()
314 }
315}
316
317#[derive(Clone, Debug)]
319#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
320#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
321pub struct VertexBufferLayout<'a> {
322 pub array_stride: wgt::BufferAddress,
324 pub step_mode: wgt::VertexStepMode,
326 pub attributes: Cow<'a, [wgt::VertexAttribute]>,
328}
329
330#[derive(Clone, Debug)]
332#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
333pub struct VertexState<'a> {
334 pub stage: ProgrammableStageDescriptor<'a>,
336 pub buffers: Cow<'a, [VertexBufferLayout<'a>]>,
338}
339
340#[derive(Clone, Debug)]
342pub struct ResolvedVertexState<'a> {
343 pub stage: ResolvedProgrammableStageDescriptor<'a>,
345 pub buffers: Cow<'a, [VertexBufferLayout<'a>]>,
347}
348
349#[derive(Clone, Debug)]
351#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
352pub struct FragmentState<'a> {
353 pub stage: ProgrammableStageDescriptor<'a>,
355 pub targets: Cow<'a, [Option<wgt::ColorTargetState>]>,
357}
358
359#[derive(Clone, Debug)]
361pub struct ResolvedFragmentState<'a> {
362 pub stage: ResolvedProgrammableStageDescriptor<'a>,
364 pub targets: Cow<'a, [Option<wgt::ColorTargetState>]>,
366}
367
368#[derive(Clone, Debug)]
370#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
371pub struct RenderPipelineDescriptor<'a> {
372 pub label: Label<'a>,
373 pub layout: Option<PipelineLayoutId>,
375 pub vertex: VertexState<'a>,
377 #[cfg_attr(feature = "serde", serde(default))]
379 pub primitive: wgt::PrimitiveState,
380 #[cfg_attr(feature = "serde", serde(default))]
382 pub depth_stencil: Option<wgt::DepthStencilState>,
383 #[cfg_attr(feature = "serde", serde(default))]
385 pub multisample: wgt::MultisampleState,
386 pub fragment: Option<FragmentState<'a>>,
388 pub multiview: Option<NonZeroU32>,
391 pub cache: Option<PipelineCacheId>,
393}
394
395#[derive(Clone, Debug)]
397pub struct ResolvedRenderPipelineDescriptor<'a> {
398 pub label: Label<'a>,
399 pub layout: Option<Arc<PipelineLayout>>,
401 pub vertex: ResolvedVertexState<'a>,
403 pub primitive: wgt::PrimitiveState,
405 pub depth_stencil: Option<wgt::DepthStencilState>,
407 pub multisample: wgt::MultisampleState,
409 pub fragment: Option<ResolvedFragmentState<'a>>,
411 pub multiview: Option<NonZeroU32>,
414 pub cache: Option<Arc<PipelineCache>>,
416}
417
418#[derive(Clone, Debug)]
419#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
420pub struct PipelineCacheDescriptor<'a> {
421 pub label: Label<'a>,
422 pub data: Option<Cow<'a, [u8]>>,
423 pub fallback: bool,
424}
425
426#[derive(Clone, Debug, Error)]
427#[non_exhaustive]
428pub enum ColorStateError {
429 #[error("Format {0:?} is not renderable")]
430 FormatNotRenderable(wgt::TextureFormat),
431 #[error("Format {0:?} is not blendable")]
432 FormatNotBlendable(wgt::TextureFormat),
433 #[error("Format {0:?} does not have a color aspect")]
434 FormatNotColor(wgt::TextureFormat),
435 #[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:?}.")]
436 InvalidSampleCount(u32, wgt::TextureFormat, Vec<u32>, Vec<u32>),
437 #[error("Output format {pipeline} is incompatible with the shader {shader}")]
438 IncompatibleFormat {
439 pipeline: validation::NumericType,
440 shader: validation::NumericType,
441 },
442 #[error("Blend factors for {0:?} must be `One`")]
443 InvalidMinMaxBlendFactors(wgt::BlendComponent),
444 #[error("Invalid write mask {0:?}")]
445 InvalidWriteMask(wgt::ColorWrites),
446}
447
448#[derive(Clone, Debug, Error)]
449#[non_exhaustive]
450pub enum DepthStencilStateError {
451 #[error("Format {0:?} is not renderable")]
452 FormatNotRenderable(wgt::TextureFormat),
453 #[error("Format {0:?} does not have a depth aspect, but depth test/write is enabled")]
454 FormatNotDepth(wgt::TextureFormat),
455 #[error("Format {0:?} does not have a stencil aspect, but stencil test/write is enabled")]
456 FormatNotStencil(wgt::TextureFormat),
457 #[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:?}.")]
458 InvalidSampleCount(u32, wgt::TextureFormat, Vec<u32>, Vec<u32>),
459}
460
461#[derive(Clone, Debug, Error)]
462#[non_exhaustive]
463pub enum CreateRenderPipelineError {
464 #[error(transparent)]
465 ColorAttachment(#[from] ColorAttachmentError),
466 #[error(transparent)]
467 Device(#[from] DeviceError),
468 #[error("Unable to derive an implicit layout")]
469 Implicit(#[from] ImplicitLayoutError),
470 #[error("Color state [{0}] is invalid")]
471 ColorState(u8, #[source] ColorStateError),
472 #[error("Depth/stencil state is invalid")]
473 DepthStencilState(#[from] DepthStencilStateError),
474 #[error("Invalid sample count {0}")]
475 InvalidSampleCount(u32),
476 #[error("The number of vertex buffers {given} exceeds the limit {limit}")]
477 TooManyVertexBuffers { given: u32, limit: u32 },
478 #[error("The total number of vertex attributes {given} exceeds the limit {limit}")]
479 TooManyVertexAttributes { given: u32, limit: u32 },
480 #[error("Vertex buffer {index} stride {given} exceeds the limit {limit}")]
481 VertexStrideTooLarge { index: u32, given: u32, limit: u32 },
482 #[error("Vertex buffer {index} stride {stride} does not respect `VERTEX_STRIDE_ALIGNMENT`")]
483 UnalignedVertexStride {
484 index: u32,
485 stride: wgt::BufferAddress,
486 },
487 #[error("Vertex attribute at location {location} has invalid offset {offset}")]
488 InvalidVertexAttributeOffset {
489 location: wgt::ShaderLocation,
490 offset: wgt::BufferAddress,
491 },
492 #[error("Two or more vertex attributes were assigned to the same location in the shader: {0}")]
493 ShaderLocationClash(u32),
494 #[error("Strip index format was not set to None but to {strip_index_format:?} while using the non-strip topology {topology:?}")]
495 StripIndexFormatForNonStripTopology {
496 strip_index_format: Option<wgt::IndexFormat>,
497 topology: wgt::PrimitiveTopology,
498 },
499 #[error("Conservative Rasterization is only supported for wgt::PolygonMode::Fill")]
500 ConservativeRasterizationNonFillPolygonMode,
501 #[error(transparent)]
502 MissingFeatures(#[from] MissingFeatures),
503 #[error(transparent)]
504 MissingDownlevelFlags(#[from] MissingDownlevelFlags),
505 #[error("Error matching {stage:?} shader requirements against the pipeline")]
506 Stage {
507 stage: wgt::ShaderStages,
508 #[source]
509 error: validation::StageError,
510 },
511 #[error("Internal error in {stage:?} shader: {error}")]
512 Internal {
513 stage: wgt::ShaderStages,
514 error: String,
515 },
516 #[error("Pipeline constant error in {stage:?} shader: {error}")]
517 PipelineConstants {
518 stage: wgt::ShaderStages,
519 error: String,
520 },
521 #[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.")]
522 UnalignedShader { group: u32, binding: u32, size: u64 },
523 #[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.")]
524 BlendFactorOnUnsupportedTarget {
525 factor: wgt::BlendFactor,
526 target: u32,
527 },
528 #[error("Pipeline expects the shader entry point to make use of dual-source blending.")]
529 PipelineExpectsShaderToUseDualSourceBlending,
530 #[error("Shader entry point expects the pipeline to make use of dual-source blending.")]
531 ShaderExpectsPipelineToUseDualSourceBlending,
532 #[error("{}", concat!(
533 "At least one color attachment or depth-stencil attachment was expected, ",
534 "but no render target for the pipeline was specified."
535 ))]
536 NoTargetSpecified,
537 #[error(transparent)]
538 InvalidResource(#[from] InvalidResourceError),
539}
540
541bitflags::bitflags! {
542 #[repr(transparent)]
543 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
544 pub struct PipelineFlags: u32 {
545 const BLEND_CONSTANT = 1 << 0;
546 const STENCIL_REFERENCE = 1 << 1;
547 const WRITES_DEPTH = 1 << 2;
548 const WRITES_STENCIL = 1 << 3;
549 }
550}
551
552#[derive(Clone, Copy, Debug)]
554pub struct VertexStep {
555 pub stride: wgt::BufferAddress,
557
558 pub last_stride: wgt::BufferAddress,
560
561 pub mode: wgt::VertexStepMode,
563}
564
565impl Default for VertexStep {
566 fn default() -> Self {
567 Self {
568 stride: 0,
569 last_stride: 0,
570 mode: wgt::VertexStepMode::Vertex,
571 }
572 }
573}
574
575#[derive(Debug)]
576pub struct RenderPipeline {
577 pub(crate) raw: ManuallyDrop<Box<dyn hal::DynRenderPipeline>>,
578 pub(crate) device: Arc<Device>,
579 pub(crate) layout: Arc<PipelineLayout>,
580 pub(crate) _shader_modules: ArrayVec<Arc<ShaderModule>, { hal::MAX_CONCURRENT_SHADER_STAGES }>,
581 pub(crate) pass_context: RenderPassContext,
582 pub(crate) flags: PipelineFlags,
583 pub(crate) strip_index_format: Option<wgt::IndexFormat>,
584 pub(crate) vertex_steps: Vec<VertexStep>,
585 pub(crate) late_sized_buffer_groups: ArrayVec<LateSizedBufferGroup, { hal::MAX_BIND_GROUPS }>,
586 pub(crate) label: String,
588 pub(crate) tracking_data: TrackingData,
589}
590
591impl Drop for RenderPipeline {
592 fn drop(&mut self) {
593 resource_log!("Destroy raw {}", self.error_ident());
594 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
596 unsafe {
597 self.device.raw().destroy_render_pipeline(raw);
598 }
599 }
600}
601
602crate::impl_resource_type!(RenderPipeline);
603crate::impl_labeled!(RenderPipeline);
604crate::impl_parent_device!(RenderPipeline);
605crate::impl_storage_item!(RenderPipeline);
606crate::impl_trackable!(RenderPipeline);
607
608impl RenderPipeline {
609 pub(crate) fn raw(&self) -> &dyn hal::DynRenderPipeline {
610 self.raw.as_ref()
611 }
612}