1use alloc::{
2 borrow::{Cow, ToOwned},
3 boxed::Box,
4 string::String,
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;
13use wgt::error::{ErrorType, WebGpuError};
14
15pub use crate::pipeline_cache::PipelineCacheValidationError;
16use crate::{
17 binding_model::{
18 BindGroupLayout, CreateBindGroupLayoutError, CreatePipelineLayoutError,
19 GetBindGroupLayoutError, PipelineLayout,
20 },
21 command::ColorAttachmentError,
22 device::{
23 AttachmentData, Device, DeviceError, MissingDownlevelFlags, MissingFeatures,
24 RenderPassContext,
25 },
26 id::{PipelineCacheId, PipelineLayoutId, ShaderModuleId},
27 resource::{InvalidResourceError, Labeled, ResourceState, TrackingData},
28 resource_log,
29 validation::{self, ShaderMetaData},
30 Label,
31};
32
33#[derive(Debug, Default)]
37pub(crate) struct LateSizedBufferGroup {
38 pub(crate) shader_sizes: Vec<wgt::BufferAddress>,
40}
41
42#[allow(clippy::large_enum_variant)]
43pub enum ShaderModuleSource<'a> {
44 #[cfg(feature = "wgsl")]
45 Wgsl(Cow<'a, str>),
46 #[cfg(feature = "glsl")]
47 Glsl(Cow<'a, str>, naga::front::glsl::Options),
48 #[cfg(feature = "spirv")]
49 SpirV(Cow<'a, [u32]>, naga::front::spv::Options),
50 Naga(Cow<'static, naga::Module>),
51 #[doc(hidden)]
54 Dummy(PhantomData<&'a ()>),
55}
56
57#[derive(Clone, Debug)]
58#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
59pub struct ShaderModuleDescriptor<'a> {
60 pub label: Label<'a>,
61 #[cfg_attr(feature = "serde", serde(default))]
62 pub runtime_checks: wgt::ShaderRuntimeChecks,
63}
64
65pub type ShaderModuleDescriptorPassthrough<'a> =
66 wgt::CreateShaderModuleDescriptorPassthrough<'a, Label<'a>>;
67
68#[derive(Debug)]
69pub struct ShaderModule {
70 pub(crate) raw: ManuallyDrop<Box<dyn hal::DynShaderModule>>,
71 pub(crate) device: Arc<Device>,
72 pub(crate) interface: ShaderMetaData,
73 pub(crate) label: String,
75}
76
77impl Drop for ShaderModule {
78 fn drop(&mut self) {
79 resource_log!("Destroy raw {}", self.error_ident());
80 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
82 unsafe {
83 self.device.raw().destroy_shader_module(raw);
84 }
85 }
86}
87
88crate::impl_resource_type!(ShaderModule);
89crate::impl_labeled!(ShaderModule);
90crate::impl_parent_device!(ShaderModule);
91crate::impl_storage_item!(ShaderModule);
92
93impl ShaderModule {
94 pub(crate) fn raw(&self) -> &dyn hal::DynShaderModule {
95 self.raw.as_ref()
96 }
97
98 pub(crate) fn finalize_entry_point_name(
99 &self,
100 stage: naga::ShaderStage,
101 entry_point: Option<&str>,
102 ) -> Result<String, validation::StageError> {
103 match self.interface {
104 ShaderMetaData::Interface(ref interface) => {
105 interface.finalize_entry_point_name(stage, entry_point)
106 }
107 ShaderMetaData::Passthrough(ref interface) => {
108 if let Some(ep) = entry_point {
109 if interface.entry_point_names.contains(ep) {
110 Ok(ep.to_owned())
111 } else {
112 Err(validation::StageError::MissingEntryPoint(ep.to_owned()))
113 }
114 } else {
115 if interface.entry_point_names.len() != 1 {
116 return Err(validation::StageError::MultipleEntryPointsFound);
117 }
118 Ok(interface
119 .entry_point_names
120 .iter()
121 .next()
122 .unwrap()
123 .to_owned())
124 }
125 }
126 }
127 }
128}
129
130#[derive(Clone, Debug, Error)]
132#[non_exhaustive]
133pub enum CreateShaderModuleError {
134 #[cfg(feature = "wgsl")]
135 #[error(transparent)]
136 Parsing(#[from] ShaderError<naga::front::wgsl::ParseError>),
137 #[cfg(feature = "glsl")]
138 #[error(transparent)]
139 ParsingGlsl(#[from] ShaderError<naga::front::glsl::ParseErrors>),
140 #[cfg(feature = "spirv")]
141 #[error(transparent)]
142 ParsingSpirV(#[from] ShaderError<naga::front::spv::Error>),
143 #[error("Failed to generate the backend-specific code")]
144 Generation,
145 #[error(transparent)]
146 Device(#[from] DeviceError),
147 #[error(transparent)]
148 Validation(#[from] ShaderError<naga::WithSpan<naga::valid::ValidationError>>),
149 #[error(transparent)]
150 MissingFeatures(#[from] MissingFeatures),
151 #[error(
152 "Shader global {bind:?} uses a group index {group} that exceeds the max_bind_groups limit of {limit}."
153 )]
154 InvalidGroupIndex {
155 bind: naga::ResourceBinding,
156 group: u32,
157 limit: u32,
158 },
159 #[error("Generic shader passthrough does not contain any code compatible with this backend.")]
160 NotCompiledForBackend,
161 #[error(
162 "Generic passthrough shaders which use GLSL or DXIL must contain exactly one entry point."
163 )]
164 IncorrectPassthroughEntryPointCount,
165}
166
167impl WebGpuError for CreateShaderModuleError {
168 fn webgpu_error_type(&self) -> ErrorType {
169 match self {
170 Self::Device(e) => e.webgpu_error_type(),
171 Self::MissingFeatures(e) => e.webgpu_error_type(),
172
173 Self::Generation => ErrorType::Internal,
174
175 Self::Validation(..)
176 | Self::InvalidGroupIndex { .. }
177 | Self::IncorrectPassthroughEntryPointCount
178 | Self::NotCompiledForBackend => ErrorType::Validation,
179 #[cfg(feature = "wgsl")]
180 Self::Parsing(..) => ErrorType::Validation,
181 #[cfg(feature = "glsl")]
182 Self::ParsingGlsl(..) => ErrorType::Validation,
183 #[cfg(feature = "spirv")]
184 Self::ParsingSpirV(..) => ErrorType::Validation,
185 }
186 }
187}
188
189#[derive(Clone, Debug)]
191#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
192pub struct ProgrammableStageDescriptor<'a, SM = ShaderModuleId> {
193 pub module: SM,
195 pub entry_point: Option<Cow<'a, str>>,
202 pub constants: naga::back::PipelineConstants,
210 pub zero_initialize_workgroup_memory: bool,
215}
216
217pub type ResolvedProgrammableStageDescriptor<'a> =
219 ProgrammableStageDescriptor<'a, Arc<ShaderModule>>;
220
221pub type ImplicitBindGroupCount = u8;
223
224#[derive(Clone, Debug, Error)]
225#[non_exhaustive]
226pub enum ImplicitLayoutError {
227 #[error("Unable to reflect the shader {0:?} interface")]
228 ReflectionError(wgt::ShaderStages),
229 #[error(transparent)]
230 BindGroup(#[from] CreateBindGroupLayoutError),
231 #[error(transparent)]
232 Pipeline(#[from] CreatePipelineLayoutError),
233 #[error("Unable to create implicit pipeline layout from passthrough shader stage: {0:?}")]
234 Passthrough(wgt::ShaderStages),
235}
236
237impl WebGpuError for ImplicitLayoutError {
238 fn webgpu_error_type(&self) -> ErrorType {
239 match self {
240 Self::ReflectionError(_) => ErrorType::Validation,
241 Self::BindGroup(e) => e.webgpu_error_type(),
242 Self::Pipeline(e) => e.webgpu_error_type(),
243 Self::Passthrough(_) => ErrorType::Validation,
244 }
245 }
246}
247
248#[derive(Clone, Debug)]
250#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
251pub struct ComputePipelineDescriptor<
252 'a,
253 PLL = PipelineLayoutId,
254 SM = ShaderModuleId,
255 PLC = PipelineCacheId,
256> {
257 pub label: Label<'a>,
258 pub layout: Option<PLL>,
260 pub stage: ProgrammableStageDescriptor<'a, SM>,
262 pub cache: Option<PLC>,
264}
265
266pub type ResolvedComputePipelineDescriptor<'a> =
268 ComputePipelineDescriptor<'a, Arc<PipelineLayout>, Arc<ShaderModule>, Arc<PipelineCache>>;
269
270#[derive(Clone, Debug, Error)]
271#[non_exhaustive]
272pub enum CreateComputePipelineError {
273 #[error(transparent)]
274 Device(#[from] DeviceError),
275 #[error("Unable to derive an implicit layout")]
276 Implicit(#[from] ImplicitLayoutError),
277 #[error("Error matching shader requirements against the pipeline")]
278 Stage(#[from] validation::StageError),
279 #[error("Internal error: {0}")]
280 Internal(String),
281 #[error("Pipeline constant error: {0}")]
282 PipelineConstants(String),
283 #[error(transparent)]
284 MissingDownlevelFlags(#[from] MissingDownlevelFlags),
285 #[error(transparent)]
286 InvalidResource(#[from] InvalidResourceError),
287}
288
289impl WebGpuError for CreateComputePipelineError {
290 fn webgpu_error_type(&self) -> ErrorType {
291 match self {
292 Self::Device(e) => e.webgpu_error_type(),
293 Self::InvalidResource(e) => e.webgpu_error_type(),
294 Self::MissingDownlevelFlags(e) => e.webgpu_error_type(),
295 Self::Implicit(e) => e.webgpu_error_type(),
296 Self::Stage(e) => e.webgpu_error_type(),
297 Self::Internal(_) => ErrorType::Internal,
298 Self::PipelineConstants(_) => ErrorType::Validation,
299 }
300 }
301}
302
303#[derive(Debug)]
304pub struct ComputePipelineState {
305 pub(crate) raw: ManuallyDrop<Box<dyn hal::DynComputePipeline>>,
306 pub(crate) layout: Arc<PipelineLayout>,
307 pub(crate) _shader_module: Arc<ShaderModule>,
308}
309
310#[derive(Debug)]
311pub struct ComputePipeline {
312 pub(crate) state: ResourceState<ComputePipelineState>,
313 pub(crate) device: Arc<Device>,
314 pub(crate) late_sized_buffer_groups: ArrayVec<LateSizedBufferGroup, { hal::MAX_BIND_GROUPS }>,
315 pub(crate) immediate_slots_required: naga::valid::ImmediateSlots,
316 pub(crate) label: String,
318 pub(crate) tracking_data: TrackingData,
319}
320
321impl Drop for ComputePipeline {
322 fn drop(&mut self) {
323 resource_log!("Destroy raw {}", self.error_ident());
324 #[cfg(feature = "trace")]
325 {
326 use crate::device::trace;
327 if let Some(t) = self.device.trace.lock().as_mut() {
328 t.add(trace::Action::DropComputePipeline(unsafe {
329 trace::to_trace(self)
330 }));
331 }
332 }
333 let ResourceState::Valid(state) = &mut self.state else {
334 return;
335 };
336 let raw = unsafe { ManuallyDrop::take(&mut state.raw) };
338 unsafe {
339 self.device.raw().destroy_compute_pipeline(raw);
340 }
341 }
342}
343
344crate::impl_resource_type!(ComputePipeline);
345crate::impl_labeled!(ComputePipeline);
346crate::impl_parent_device!(ComputePipeline);
347crate::impl_storage_item!(ComputePipeline);
348crate::impl_trackable!(ComputePipeline);
349
350impl ComputePipeline {
351 pub(crate) fn raw(&self) -> Result<&dyn hal::DynComputePipeline, InvalidResourceError> {
352 let ResourceState::Valid(state) = &self.state else {
353 return Err(InvalidResourceError(self.error_ident()));
354 };
355 Ok(state.raw.as_ref())
356 }
357
358 pub(crate) fn layout(&self) -> Result<&Arc<PipelineLayout>, InvalidResourceError> {
359 let ResourceState::Valid(state) = &self.state else {
360 return Err(InvalidResourceError(self.error_ident()));
361 };
362 Ok(&state.layout)
363 }
364
365 pub(crate) fn check_valid(&self) -> Result<(), InvalidResourceError> {
366 let ResourceState::Valid(_) = &self.state else {
367 return Err(InvalidResourceError(self.error_ident()));
368 };
369 Ok(())
370 }
371
372 pub(crate) fn invalid(device: Arc<Device>, label: String) -> Arc<Self> {
373 Arc::new(Self {
374 tracking_data: TrackingData::new(device.tracker_indices.compute_pipelines.clone()),
375 state: ResourceState::Invalid,
376 device,
377 late_sized_buffer_groups: ArrayVec::new(),
378 immediate_slots_required: naga::valid::ImmediateSlots::default(),
379 label,
380 })
381 }
382
383 pub fn get_bind_group_layout_inner(
384 self: &Arc<Self>,
385 index: u32,
386 ) -> Result<Arc<BindGroupLayout>, GetBindGroupLayoutError> {
387 self.layout()?.get_bind_group_layout(index, self.into())
388 }
389
390 pub fn get_bind_group_layout(
391 self: &Arc<Self>,
392 index: u32,
393 ) -> (Arc<BindGroupLayout>, Option<GetBindGroupLayoutError>) {
394 let (bgl, error) = match self.get_bind_group_layout_inner(index) {
395 Ok(bgl) => (bgl, None),
396 Err(e) => (
397 BindGroupLayout::invalid(&self.device, String::new()),
398 Some(e),
399 ),
400 };
401 #[cfg(feature = "trace")]
402 if let Some(ref mut trace) = *self.device.trace.lock() {
403 use crate::device::trace;
404 use trace::IntoTrace;
405 trace.add(trace::Action::GetComputePipelineBindGroupLayout {
406 id: bgl.to_trace(),
407 pipeline: self.to_trace(),
408 index,
409 });
410 };
411 (bgl, error)
412 }
413}
414
415#[derive(Clone, Debug, Error)]
416#[non_exhaustive]
417pub enum CreatePipelineCacheError {
418 #[error(transparent)]
419 Device(#[from] DeviceError),
420 #[error("Pipeline cache validation failed")]
421 Validation(#[from] PipelineCacheValidationError),
422 #[error(transparent)]
423 MissingFeatures(#[from] MissingFeatures),
424}
425
426impl WebGpuError for CreatePipelineCacheError {
427 fn webgpu_error_type(&self) -> ErrorType {
428 match self {
429 Self::Device(e) => e.webgpu_error_type(),
430 Self::Validation(e) => e.webgpu_error_type(),
431 Self::MissingFeatures(e) => e.webgpu_error_type(),
432 }
433 }
434}
435
436#[derive(Debug)]
437pub struct PipelineCache {
438 pub(crate) raw: ManuallyDrop<Box<dyn hal::DynPipelineCache>>,
439 pub(crate) device: Arc<Device>,
440 pub(crate) label: String,
442}
443
444impl Drop for PipelineCache {
445 fn drop(&mut self) {
446 resource_log!("Destroy raw {}", self.error_ident());
447 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
449 unsafe {
450 self.device.raw().destroy_pipeline_cache(raw);
451 }
452 }
453}
454
455crate::impl_resource_type!(PipelineCache);
456crate::impl_labeled!(PipelineCache);
457crate::impl_parent_device!(PipelineCache);
458crate::impl_storage_item!(PipelineCache);
459
460impl PipelineCache {
461 pub(crate) fn raw(&self) -> &dyn hal::DynPipelineCache {
462 self.raw.as_ref()
463 }
464}
465
466#[derive(Clone, Debug)]
468#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
469#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
470pub struct VertexBufferLayout<'a> {
471 pub array_stride: wgt::BufferAddress,
473 pub step_mode: wgt::VertexStepMode,
475 pub attributes: Cow<'a, [wgt::VertexAttribute]>,
477}
478
479impl Default for VertexBufferLayout<'_> {
481 fn default() -> Self {
482 Self {
483 array_stride: Default::default(),
484 step_mode: Default::default(),
485 attributes: Cow::Borrowed(&[]),
486 }
487 }
488}
489
490#[derive(Clone, Debug)]
492#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
493pub struct VertexState<'a, SM = ShaderModuleId> {
494 pub stage: ProgrammableStageDescriptor<'a, SM>,
496 pub buffers: Cow<'a, [Option<VertexBufferLayout<'a>>]>,
498}
499
500pub type ResolvedVertexState<'a> = VertexState<'a, Arc<ShaderModule>>;
502
503#[derive(Clone, Debug)]
505#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
506pub struct FragmentState<'a, SM = ShaderModuleId> {
507 pub stage: ProgrammableStageDescriptor<'a, SM>,
509 pub targets: Cow<'a, [Option<wgt::ColorTargetState>]>,
511}
512
513pub type ResolvedFragmentState<'a> = FragmentState<'a, Arc<ShaderModule>>;
515
516#[derive(Clone, Debug)]
518#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
519pub struct TaskState<'a, SM = ShaderModuleId> {
520 pub stage: ProgrammableStageDescriptor<'a, SM>,
522}
523
524pub type ResolvedTaskState<'a> = TaskState<'a, Arc<ShaderModule>>;
525
526#[derive(Clone, Debug)]
528#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
529pub struct MeshState<'a, SM = ShaderModuleId> {
530 pub stage: ProgrammableStageDescriptor<'a, SM>,
532}
533
534pub type ResolvedMeshState<'a> = MeshState<'a, Arc<ShaderModule>>;
535
536#[doc(hidden)]
544#[derive(Clone, Debug)]
545#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
546pub enum RenderPipelineVertexProcessor<'a, SM = ShaderModuleId> {
547 Vertex(VertexState<'a, SM>),
548 Mesh(Option<TaskState<'a, SM>>, MeshState<'a, SM>),
549}
550
551#[derive(Clone, Debug)]
553#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
554pub struct RenderPipelineDescriptor<
555 'a,
556 PLL = PipelineLayoutId,
557 SM = ShaderModuleId,
558 PLC = PipelineCacheId,
559> {
560 pub label: Label<'a>,
561 pub layout: Option<PLL>,
563 pub vertex: VertexState<'a, SM>,
565 #[cfg_attr(feature = "serde", serde(default))]
567 pub primitive: wgt::PrimitiveState,
568 #[cfg_attr(feature = "serde", serde(default))]
570 pub depth_stencil: Option<wgt::DepthStencilState>,
571 #[cfg_attr(feature = "serde", serde(default))]
573 pub multisample: wgt::MultisampleState,
574 pub fragment: Option<FragmentState<'a, SM>>,
576 pub multiview_mask: Option<NonZeroU32>,
579 pub cache: Option<PLC>,
581}
582#[derive(Clone, Debug)]
584#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
585pub struct MeshPipelineDescriptor<
586 'a,
587 PLL = PipelineLayoutId,
588 SM = ShaderModuleId,
589 PLC = PipelineCacheId,
590> {
591 pub label: Label<'a>,
592 pub layout: Option<PLL>,
594 pub task: Option<TaskState<'a, SM>>,
596 pub mesh: MeshState<'a, SM>,
598 #[cfg_attr(feature = "serde", serde(default))]
600 pub primitive: wgt::PrimitiveState,
601 #[cfg_attr(feature = "serde", serde(default))]
603 pub depth_stencil: Option<wgt::DepthStencilState>,
604 #[cfg_attr(feature = "serde", serde(default))]
606 pub multisample: wgt::MultisampleState,
607 pub fragment: Option<FragmentState<'a, SM>>,
609 pub multiview: Option<NonZeroU32>,
612 pub cache: Option<PLC>,
614}
615
616#[doc(hidden)]
624#[derive(Clone, Debug)]
625#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
626pub struct GeneralRenderPipelineDescriptor<
627 'a,
628 PLL = PipelineLayoutId,
629 SM = ShaderModuleId,
630 PLC = PipelineCacheId,
631> {
632 pub label: Label<'a>,
633 pub layout: Option<PLL>,
635 pub vertex: RenderPipelineVertexProcessor<'a, SM>,
637 #[cfg_attr(feature = "serde", serde(default))]
639 pub primitive: wgt::PrimitiveState,
640 #[cfg_attr(feature = "serde", serde(default))]
642 pub depth_stencil: Option<wgt::DepthStencilState>,
643 #[cfg_attr(feature = "serde", serde(default))]
645 pub multisample: wgt::MultisampleState,
646 pub fragment: Option<FragmentState<'a, SM>>,
648 pub multiview_mask: Option<NonZeroU32>,
651 pub cache: Option<PLC>,
653}
654impl<'a, PLL, SM, PLC> From<RenderPipelineDescriptor<'a, PLL, SM, PLC>>
655 for GeneralRenderPipelineDescriptor<'a, PLL, SM, PLC>
656{
657 fn from(value: RenderPipelineDescriptor<'a, PLL, SM, PLC>) -> Self {
658 Self {
659 label: value.label,
660 layout: value.layout,
661 vertex: RenderPipelineVertexProcessor::Vertex(value.vertex),
662 primitive: value.primitive,
663 depth_stencil: value.depth_stencil,
664 multisample: value.multisample,
665 fragment: value.fragment,
666 multiview_mask: value.multiview_mask,
667 cache: value.cache,
668 }
669 }
670}
671impl<'a, PLL, SM, PLC> From<MeshPipelineDescriptor<'a, PLL, SM, PLC>>
672 for GeneralRenderPipelineDescriptor<'a, PLL, SM, PLC>
673{
674 fn from(value: MeshPipelineDescriptor<'a, PLL, SM, PLC>) -> Self {
675 Self {
676 label: value.label,
677 layout: value.layout,
678 vertex: RenderPipelineVertexProcessor::Mesh(value.task, value.mesh),
679 primitive: value.primitive,
680 depth_stencil: value.depth_stencil,
681 multisample: value.multisample,
682 fragment: value.fragment,
683 multiview_mask: value.multiview,
684 cache: value.cache,
685 }
686 }
687}
688
689pub type ResolvedGeneralRenderPipelineDescriptor<'a> =
693 GeneralRenderPipelineDescriptor<'a, Arc<PipelineLayout>, Arc<ShaderModule>, Arc<PipelineCache>>;
694
695#[derive(Clone, Debug)]
696#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
697pub struct PipelineCacheDescriptor<'a> {
698 pub label: Label<'a>,
699 pub data: Option<Cow<'a, [u8]>>,
700 pub fallback: bool,
701}
702
703#[derive(Clone, Debug, Error)]
704#[non_exhaustive]
705pub enum ColorStateError {
706 #[error("Format {0:?} is not renderable")]
707 FormatNotRenderable(wgt::TextureFormat),
708 #[error("Format {0:?} is not blendable")]
709 FormatNotBlendable(wgt::TextureFormat),
710 #[error("Format {0:?} does not have a color aspect")]
711 FormatNotColor(wgt::TextureFormat),
712 #[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:?}.")]
713 InvalidSampleCount(u32, wgt::TextureFormat, Vec<u32>, Vec<u32>),
714 #[error("Output format {pipeline} is incompatible with the shader {shader}")]
715 IncompatibleFormat {
716 pipeline: validation::NumericType,
717 shader: validation::NumericType,
718 },
719 #[error("Invalid write mask {0:?}")]
720 InvalidWriteMask(wgt::ColorWrites),
721 #[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.")]
722 BlendFactorOnUnsupportedTarget {
723 factor: wgt::BlendFactor,
724 target: u32,
725 },
726 #[error(
727 "Blend factor {factor:?} for render target {target} is not valid. Blend factor must be `one` when using min/max blend operations."
728 )]
729 InvalidMinMaxBlendFactor {
730 factor: wgt::BlendFactor,
731 target: u32,
732 },
733}
734
735#[derive(Clone, Debug, Error)]
736#[non_exhaustive]
737pub enum DepthStencilStateError {
738 #[error("Format {0:?} is not renderable")]
739 FormatNotRenderable(wgt::TextureFormat),
740 #[error("Format {0:?} is not a depth/stencil format")]
741 FormatNotDepthOrStencil(wgt::TextureFormat),
742 #[error("Format {0:?} does not have a depth aspect, but depth test/write is enabled")]
743 FormatNotDepth(wgt::TextureFormat),
744 #[error("Format {0:?} does not have a stencil aspect, but stencil test/write is enabled")]
745 FormatNotStencil(wgt::TextureFormat),
746 #[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:?}.")]
747 InvalidSampleCount(u32, wgt::TextureFormat, Vec<u32>, Vec<u32>),
748 #[error("Depth bias is not compatible with non-triangle topology {0:?}")]
749 DepthBiasWithIncompatibleTopology(wgt::PrimitiveTopology),
750 #[error("Depth compare function must be specified for depth format {0:?}")]
751 MissingDepthCompare(wgt::TextureFormat),
752 #[error("Depth write enabled must be specified for depth format {0:?}")]
753 MissingDepthWriteEnabled(wgt::TextureFormat),
754}
755
756#[derive(Clone, Debug, Error)]
757#[non_exhaustive]
758pub enum CreateRenderPipelineError {
759 #[error(transparent)]
760 ColorAttachment(#[from] ColorAttachmentError),
761 #[error(transparent)]
762 Device(#[from] DeviceError),
763 #[error("Unable to derive an implicit layout")]
764 Implicit(#[from] ImplicitLayoutError),
765 #[error("Color state [{0}] is invalid")]
766 ColorState(u8, #[source] ColorStateError),
767 #[error("Depth/stencil state is invalid")]
768 DepthStencilState(#[from] DepthStencilStateError),
769 #[error("Invalid sample count {0}")]
770 InvalidSampleCount(u32),
771 #[error("The number of vertex buffers {given} exceeds the limit {limit}")]
772 TooManyVertexBuffers { given: u32, limit: u32 },
773 #[error("The number of bind groups + vertex buffers {given} exceeds the limit {limit}")]
774 TooManyBindGroupsPlusVertexBuffers { given: u32, limit: u32 },
775 #[error("The number of vertex-stage buffers and acceleration structures {given} exceeds the limit {limit}")]
776 TooManyBuffersAndAccelerationStructuresInVertexStage { given: u32, limit: u32 },
777 #[error("The total number of vertex attributes {given} exceeds the limit {limit}")]
778 TooManyVertexAttributes { given: u32, limit: u32 },
779 #[error("Vertex attribute location {given} must be less than limit {limit}")]
780 VertexAttributeLocationTooLarge { given: u32, limit: u32 },
781 #[error("Vertex buffer {index} stride {given} exceeds the limit {limit}")]
782 VertexStrideTooLarge { index: u32, given: u32, limit: u32 },
783 #[error("Vertex attribute at location {location} stride {given} exceeds the limit {limit}")]
784 VertexAttributeStrideTooLarge {
785 location: wgt::ShaderLocation,
786 given: u32,
787 limit: u32,
788 },
789 #[error("Vertex buffer {index} stride {stride} does not respect `VERTEX_ALIGNMENT`")]
790 UnalignedVertexStride {
791 index: u32,
792 stride: wgt::BufferAddress,
793 },
794 #[error("Vertex attribute at location {location} has invalid offset {offset}")]
795 InvalidVertexAttributeOffset {
796 location: wgt::ShaderLocation,
797 offset: wgt::BufferAddress,
798 },
799 #[error("Two or more vertex attributes were assigned to the same location in the shader: {0}")]
800 ShaderLocationClash(u32),
801 #[error("Strip index format was not set to None but to {strip_index_format:?} while using the non-strip topology {topology:?}")]
802 StripIndexFormatForNonStripTopology {
803 strip_index_format: Option<wgt::IndexFormat>,
804 topology: wgt::PrimitiveTopology,
805 },
806 #[error("Conservative Rasterization is only supported for wgt::PolygonMode::Fill")]
807 ConservativeRasterizationNonFillPolygonMode,
808 #[error(transparent)]
809 MissingFeatures(#[from] MissingFeatures),
810 #[error(transparent)]
811 MissingDownlevelFlags(#[from] MissingDownlevelFlags),
812 #[error("Error matching {stage:?} shader requirements against the pipeline")]
813 Stage {
814 stage: wgt::ShaderStages,
815 #[source]
816 error: validation::StageError,
817 },
818 #[error("Internal error in {stage:?} shader: {error}")]
819 Internal {
820 stage: wgt::ShaderStages,
821 error: String,
822 },
823 #[error("Pipeline constant error in {stage:?} shader: {error}")]
824 PipelineConstants {
825 stage: wgt::ShaderStages,
826 error: String,
827 },
828 #[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.")]
829 UnalignedShader { group: u32, binding: u32, size: u64 },
830 #[error("Dual-source blending requires exactly one color target, but {count} color targets are present")]
831 DualSourceBlendingWithMultipleColorTargets { count: usize },
832 #[error("{}", concat!(
833 "At least one color attachment or depth-stencil attachment was expected, ",
834 "but no render target for the pipeline was specified."
835 ))]
836 NoTargetSpecified,
837 #[error(transparent)]
838 InvalidResource(#[from] InvalidResourceError),
839}
840
841impl WebGpuError for CreateRenderPipelineError {
842 fn webgpu_error_type(&self) -> ErrorType {
843 match self {
844 Self::Device(e) => e.webgpu_error_type(),
845 Self::InvalidResource(e) => e.webgpu_error_type(),
846 Self::MissingFeatures(e) => e.webgpu_error_type(),
847 Self::MissingDownlevelFlags(e) => e.webgpu_error_type(),
848
849 Self::Internal { .. } => ErrorType::Internal,
850
851 Self::ColorAttachment(_)
852 | Self::Implicit(_)
853 | Self::ColorState(_, _)
854 | Self::DepthStencilState(_)
855 | Self::InvalidSampleCount(_)
856 | Self::TooManyVertexBuffers { .. }
857 | Self::TooManyBindGroupsPlusVertexBuffers { .. }
858 | Self::TooManyBuffersAndAccelerationStructuresInVertexStage { .. }
859 | Self::TooManyVertexAttributes { .. }
860 | Self::VertexAttributeLocationTooLarge { .. }
861 | Self::VertexStrideTooLarge { .. }
862 | Self::UnalignedVertexStride { .. }
863 | Self::InvalidVertexAttributeOffset { .. }
864 | Self::ShaderLocationClash(_)
865 | Self::StripIndexFormatForNonStripTopology { .. }
866 | Self::ConservativeRasterizationNonFillPolygonMode
867 | Self::Stage { .. }
868 | Self::UnalignedShader { .. }
869 | Self::DualSourceBlendingWithMultipleColorTargets { .. }
870 | Self::NoTargetSpecified
871 | Self::PipelineConstants { .. }
872 | Self::VertexAttributeStrideTooLarge { .. } => ErrorType::Validation,
873 }
874 }
875}
876
877bitflags::bitflags! {
878 #[repr(transparent)]
879 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
880 pub struct PipelineFlags: u32 {
881 const BLEND_CONSTANT = 1 << 0;
882 const STENCIL_REFERENCE = 1 << 1;
883 const WRITES_DEPTH = 1 << 2;
884 const WRITES_STENCIL = 1 << 3;
885 }
886}
887
888#[derive(Clone, Copy, Debug)]
890pub struct VertexStep {
891 pub stride: wgt::BufferAddress,
893
894 pub last_stride: wgt::BufferAddress,
896
897 pub mode: wgt::VertexStepMode,
899}
900
901impl Default for VertexStep {
902 fn default() -> Self {
903 Self {
904 stride: 0,
905 last_stride: 0,
906 mode: wgt::VertexStepMode::Vertex,
907 }
908 }
909}
910
911#[derive(Debug)]
912pub(crate) struct RenderPipelineState {
913 pub(crate) raw: ManuallyDrop<Box<dyn hal::DynRenderPipeline>>,
914 pub(crate) layout: Arc<PipelineLayout>,
915}
916
917#[derive(Debug)]
918pub struct RenderPipeline {
919 pub(crate) state: ResourceState<RenderPipelineState>,
920 pub(crate) device: Arc<Device>,
921 pub(crate) _shader_modules: ArrayVec<Arc<ShaderModule>, { hal::MAX_CONCURRENT_SHADER_STAGES }>,
922 pub(crate) pass_context: RenderPassContext,
923 pub(crate) flags: PipelineFlags,
924 pub(crate) topology: wgt::PrimitiveTopology,
925 pub(crate) strip_index_format: Option<wgt::IndexFormat>,
926 pub(crate) vertex_steps: Vec<Option<VertexStep>>,
927 pub(crate) late_sized_buffer_groups: ArrayVec<LateSizedBufferGroup, { hal::MAX_BIND_GROUPS }>,
928 pub(crate) immediate_slots_required: naga::valid::ImmediateSlots,
929 pub(crate) label: String,
931 pub(crate) tracking_data: TrackingData,
932 pub(crate) is_mesh: bool,
934 pub(crate) has_task_shader: bool,
935}
936
937impl Drop for RenderPipeline {
938 fn drop(&mut self) {
939 resource_log!("Destroy raw {}", self.error_ident());
940 #[cfg(feature = "trace")]
941 {
942 use crate::device::trace;
943 if let Some(t) = self.device.trace.lock().as_mut() {
944 t.add(trace::Action::DropRenderPipeline(unsafe {
945 trace::to_trace(self)
946 }));
947 }
948 }
949 let ResourceState::Valid(state) = &mut self.state else {
950 return;
951 };
952 let raw = unsafe { ManuallyDrop::take(&mut state.raw) };
954 unsafe {
955 self.device.raw().destroy_render_pipeline(raw);
956 }
957 }
958}
959
960crate::impl_resource_type!(RenderPipeline);
961crate::impl_labeled!(RenderPipeline);
962crate::impl_parent_device!(RenderPipeline);
963crate::impl_storage_item!(RenderPipeline);
964crate::impl_trackable!(RenderPipeline);
965
966impl RenderPipeline {
967 pub(crate) fn raw(&self) -> Result<&dyn hal::DynRenderPipeline, InvalidResourceError> {
968 let ResourceState::Valid(state) = &self.state else {
969 return Err(InvalidResourceError(self.error_ident()));
970 };
971 Ok(state.raw.as_ref())
972 }
973
974 pub(crate) fn layout(&self) -> Result<&Arc<PipelineLayout>, InvalidResourceError> {
975 let ResourceState::Valid(state) = &self.state else {
976 return Err(InvalidResourceError(self.error_ident()));
977 };
978 Ok(&state.layout)
979 }
980
981 pub(crate) fn check_valid(&self) -> Result<(), InvalidResourceError> {
982 let ResourceState::Valid(_) = &self.state else {
983 return Err(InvalidResourceError(self.error_ident()));
984 };
985 Ok(())
986 }
987
988 pub(crate) fn invalid(device: Arc<Device>, label: String) -> Arc<Self> {
989 Arc::new(Self {
990 tracking_data: TrackingData::new(device.tracker_indices.render_pipelines.clone()),
991 state: ResourceState::Invalid,
992 device,
993 _shader_modules: ArrayVec::new(),
994 pass_context: RenderPassContext {
995 attachments: AttachmentData {
996 colors: ArrayVec::new(),
997 resolves: ArrayVec::new(),
998 depth_stencil: None,
999 },
1000 sample_count: 0,
1001 multiview_mask: None,
1002 },
1003 flags: PipelineFlags::empty(),
1004 topology: wgt::PrimitiveTopology::TriangleList,
1005 strip_index_format: None,
1006 vertex_steps: Vec::new(),
1007 late_sized_buffer_groups: ArrayVec::new(),
1008 immediate_slots_required: naga::valid::ImmediateSlots::default(),
1009 label,
1010 is_mesh: false,
1011 has_task_shader: false,
1012 })
1013 }
1014
1015 pub fn get_bind_group_layout_inner(
1016 self: &Arc<Self>,
1017 index: u32,
1018 ) -> Result<Arc<BindGroupLayout>, GetBindGroupLayoutError> {
1019 self.layout()?.get_bind_group_layout(index, self.into())
1020 }
1021
1022 pub fn get_bind_group_layout(
1023 self: &Arc<Self>,
1024 index: u32,
1025 ) -> (Arc<BindGroupLayout>, Option<GetBindGroupLayoutError>) {
1026 let (bgl, error) = match self.get_bind_group_layout_inner(index) {
1027 Ok(bgl) => (bgl, None),
1028 Err(e) => (
1029 BindGroupLayout::invalid(&self.device, String::new()),
1030 Some(e),
1031 ),
1032 };
1033 #[cfg(feature = "trace")]
1034 if let Some(ref mut trace) = *self.device.trace.lock() {
1035 use crate::device::trace;
1036 use trace::IntoTrace;
1037 trace.add(trace::Action::GetRenderPipelineBindGroupLayout {
1038 id: bgl.to_trace(),
1039 pipeline: self.to_trace(),
1040 index,
1041 });
1042 };
1043 (bgl, error)
1044 }
1045}