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