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