1#[cfg(feature = "trace")]
2use crate::device::trace;
3use crate::{
4 binding_model::{CreateBindGroupLayoutError, CreatePipelineLayoutError, PipelineLayout},
5 command::ColorAttachmentError,
6 device::{Device, DeviceError, MissingDownlevelFlags, MissingFeatures, RenderPassContext},
7 hal_api::HalApi,
8 id::{PipelineLayoutId, ShaderModuleId},
9 resource::{Resource, ResourceInfo, ResourceType},
10 resource_log, validation, Label,
11};
12use arrayvec::ArrayVec;
13use std::{borrow::Cow, error::Error, fmt, marker::PhantomData, num::NonZeroU32, sync::Arc};
14use thiserror::Error;
15
16#[derive(Debug)]
20pub(crate) struct LateSizedBufferGroup {
21 pub(crate) shader_sizes: Vec<wgt::BufferAddress>,
23}
24
25#[allow(clippy::large_enum_variant)]
26pub enum ShaderModuleSource<'a> {
27 #[cfg(feature = "wgsl")]
28 Wgsl(Cow<'a, str>),
29 #[cfg(feature = "glsl")]
30 Glsl(Cow<'a, str>, naga::front::glsl::Options),
31 #[cfg(feature = "spirv")]
32 SpirV(Cow<'a, [u32]>, naga::front::spv::Options),
33 Naga(Cow<'static, naga::Module>),
34 #[doc(hidden)]
37 Dummy(PhantomData<&'a ()>),
38}
39
40#[derive(Clone, Debug)]
41#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
42pub struct ShaderModuleDescriptor<'a> {
43 pub label: Label<'a>,
44 #[cfg_attr(feature = "serde", serde(default))]
45 pub shader_bound_checks: wgt::ShaderBoundChecks,
46}
47
48#[derive(Debug)]
49pub struct ShaderModule<A: HalApi> {
50 pub(crate) raw: Option<A::ShaderModule>,
51 pub(crate) device: Arc<Device<A>>,
52 pub(crate) interface: Option<validation::Interface>,
53 pub(crate) info: ResourceInfo<ShaderModule<A>>,
54 pub(crate) label: String,
55}
56
57impl<A: HalApi> Drop for ShaderModule<A> {
58 fn drop(&mut self) {
59 if let Some(raw) = self.raw.take() {
60 resource_log!("Destroy raw ShaderModule {:?}", self.info.label());
61 #[cfg(feature = "trace")]
62 if let Some(t) = self.device.trace.lock().as_mut() {
63 t.add(trace::Action::DestroyShaderModule(self.info.id()));
64 }
65 unsafe {
66 use hal::Device;
67 self.device.raw().destroy_shader_module(raw);
68 }
69 }
70 }
71}
72
73impl<A: HalApi> Resource for ShaderModule<A> {
74 const TYPE: ResourceType = "ShaderModule";
75
76 type Marker = crate::id::markers::ShaderModule;
77
78 fn as_info(&self) -> &ResourceInfo<Self> {
79 &self.info
80 }
81
82 fn as_info_mut(&mut self) -> &mut ResourceInfo<Self> {
83 &mut self.info
84 }
85
86 fn label(&self) -> String {
87 self.label.clone()
88 }
89}
90
91impl<A: HalApi> ShaderModule<A> {
92 pub(crate) fn raw(&self) -> &A::ShaderModule {
93 self.raw.as_ref().unwrap()
94 }
95
96 pub(crate) fn finalize_entry_point_name(
97 &self,
98 stage_bit: wgt::ShaderStages,
99 entry_point: Option<&str>,
100 ) -> Result<String, validation::StageError> {
101 match &self.interface {
102 Some(interface) => interface.finalize_entry_point_name(stage_bit, entry_point),
103 None => entry_point
104 .map(|ep| ep.to_string())
105 .ok_or(validation::StageError::NoEntryPointFound),
106 }
107 }
108}
109
110#[derive(Clone, Debug)]
111pub struct ShaderError<E> {
112 pub source: String,
113 pub label: Option<String>,
114 pub inner: Box<E>,
115}
116#[cfg(feature = "wgsl")]
117impl fmt::Display for ShaderError<naga::front::wgsl::ParseError> {
118 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119 let label = self.label.as_deref().unwrap_or_default();
120 let string = self.inner.emit_to_string(&self.source);
121 write!(f, "\nShader '{label}' parsing {string}")
122 }
123}
124#[cfg(feature = "glsl")]
125impl fmt::Display for ShaderError<naga::front::glsl::ParseError> {
126 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127 let label = self.label.as_deref().unwrap_or_default();
128 let string = self.inner.emit_to_string(&self.source);
129 write!(f, "\nShader '{label}' parsing {string}")
130 }
131}
132#[cfg(feature = "spirv")]
133impl fmt::Display for ShaderError<naga::front::spv::Error> {
134 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135 let label = self.label.as_deref().unwrap_or_default();
136 let string = self.inner.emit_to_string(&self.source);
137 write!(f, "\nShader '{label}' parsing {string}")
138 }
139}
140impl fmt::Display for ShaderError<naga::WithSpan<naga::valid::ValidationError>> {
141 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
142 use codespan_reporting::{
143 diagnostic::{Diagnostic, Label},
144 files::SimpleFile,
145 term,
146 };
147
148 let label = self.label.as_deref().unwrap_or_default();
149 let files = SimpleFile::new(label, &self.source);
150 let config = term::Config::default();
151 let mut writer = term::termcolor::NoColor::new(Vec::new());
152
153 let diagnostic = Diagnostic::error().with_labels(
154 self.inner
155 .spans()
156 .map(|&(span, ref desc)| {
157 Label::primary((), span.to_range().unwrap()).with_message(desc.to_owned())
158 })
159 .collect(),
160 );
161
162 term::emit(&mut writer, &config, &files, &diagnostic).expect("cannot write error");
163
164 write!(
165 f,
166 "\nShader validation {}",
167 String::from_utf8_lossy(&writer.into_inner())
168 )
169 }
170}
171impl<E> Error for ShaderError<E>
172where
173 ShaderError<E>: fmt::Display,
174 E: Error + 'static,
175{
176 fn source(&self) -> Option<&(dyn Error + 'static)> {
177 Some(&self.inner)
178 }
179}
180
181#[derive(Debug, Error)]
183#[non_exhaustive]
184pub enum CreateShaderModuleError {
185 #[cfg(feature = "wgsl")]
186 #[error(transparent)]
187 Parsing(#[from] ShaderError<naga::front::wgsl::ParseError>),
188 #[cfg(feature = "glsl")]
189 #[error(transparent)]
190 ParsingGlsl(#[from] ShaderError<naga::front::glsl::ParseError>),
191 #[cfg(feature = "spirv")]
192 #[error(transparent)]
193 ParsingSpirV(#[from] ShaderError<naga::front::spv::Error>),
194 #[error("Failed to generate the backend-specific code")]
195 Generation,
196 #[error(transparent)]
197 Device(#[from] DeviceError),
198 #[error(transparent)]
199 Validation(#[from] ShaderError<naga::WithSpan<naga::valid::ValidationError>>),
200 #[error(transparent)]
201 MissingFeatures(#[from] MissingFeatures),
202 #[error(
203 "Shader global {bind:?} uses a group index {group} that exceeds the max_bind_groups limit of {limit}."
204 )]
205 InvalidGroupIndex {
206 bind: naga::ResourceBinding,
207 group: u32,
208 limit: u32,
209 },
210}
211
212impl CreateShaderModuleError {
213 pub fn location(&self, source: &str) -> Option<naga::SourceLocation> {
214 match *self {
215 #[cfg(feature = "wgsl")]
216 CreateShaderModuleError::Parsing(ref err) => err.inner.location(source),
217 CreateShaderModuleError::Validation(ref err) => err.inner.location(source),
218 _ => None,
219 }
220 }
221}
222
223#[derive(Clone, Debug)]
225#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
226pub struct ProgrammableStageDescriptor<'a> {
227 pub module: ShaderModuleId,
229 pub entry_point: Option<Cow<'a, str>>,
236 pub constants: Cow<'a, naga::back::PipelineConstants>,
244 pub zero_initialize_workgroup_memory: bool,
249}
250
251pub type ImplicitBindGroupCount = u8;
253
254#[derive(Clone, Debug, Error)]
255#[non_exhaustive]
256pub enum ImplicitLayoutError {
257 #[error("Missing IDs for deriving {0} bind groups")]
258 MissingIds(ImplicitBindGroupCount),
259 #[error("Unable to reflect the shader {0:?} interface")]
260 ReflectionError(wgt::ShaderStages),
261 #[error(transparent)]
262 BindGroup(#[from] CreateBindGroupLayoutError),
263 #[error(transparent)]
264 Pipeline(#[from] CreatePipelineLayoutError),
265}
266
267#[derive(Clone, Debug)]
269#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
270pub struct ComputePipelineDescriptor<'a> {
271 pub label: Label<'a>,
272 pub layout: Option<PipelineLayoutId>,
274 pub stage: ProgrammableStageDescriptor<'a>,
276}
277
278#[derive(Clone, Debug, Error)]
279#[non_exhaustive]
280pub enum CreateComputePipelineError {
281 #[error(transparent)]
282 Device(#[from] DeviceError),
283 #[error("Pipeline layout is invalid")]
284 InvalidLayout,
285 #[error("Unable to derive an implicit layout")]
286 Implicit(#[from] ImplicitLayoutError),
287 #[error("Error matching shader requirements against the pipeline")]
288 Stage(#[from] validation::StageError),
289 #[error("Internal error: {0}")]
290 Internal(String),
291 #[error(transparent)]
292 MissingDownlevelFlags(#[from] MissingDownlevelFlags),
293}
294
295#[derive(Debug)]
296pub struct ComputePipeline<A: HalApi> {
297 pub(crate) raw: Option<A::ComputePipeline>,
298 pub(crate) layout: Arc<PipelineLayout<A>>,
299 pub(crate) device: Arc<Device<A>>,
300 pub(crate) _shader_module: Arc<ShaderModule<A>>,
301 pub(crate) late_sized_buffer_groups: ArrayVec<LateSizedBufferGroup, { hal::MAX_BIND_GROUPS }>,
302 pub(crate) info: ResourceInfo<ComputePipeline<A>>,
303}
304
305impl<A: HalApi> Drop for ComputePipeline<A> {
306 fn drop(&mut self) {
307 if let Some(raw) = self.raw.take() {
308 resource_log!("Destroy raw ComputePipeline {:?}", self.info.label());
309
310 #[cfg(feature = "trace")]
311 if let Some(t) = self.device.trace.lock().as_mut() {
312 t.add(trace::Action::DestroyComputePipeline(self.info.id()));
313 }
314
315 unsafe {
316 use hal::Device;
317 self.device.raw().destroy_compute_pipeline(raw);
318 }
319 }
320 }
321}
322
323impl<A: HalApi> Resource for ComputePipeline<A> {
324 const TYPE: ResourceType = "ComputePipeline";
325
326 type Marker = crate::id::markers::ComputePipeline;
327
328 fn as_info(&self) -> &ResourceInfo<Self> {
329 &self.info
330 }
331
332 fn as_info_mut(&mut self) -> &mut ResourceInfo<Self> {
333 &mut self.info
334 }
335}
336
337impl<A: HalApi> ComputePipeline<A> {
338 pub(crate) fn raw(&self) -> &A::ComputePipeline {
339 self.raw.as_ref().unwrap()
340 }
341}
342
343#[derive(Clone, Debug)]
345#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
346#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
347pub struct VertexBufferLayout<'a> {
348 pub array_stride: wgt::BufferAddress,
350 pub step_mode: wgt::VertexStepMode,
352 pub attributes: Cow<'a, [wgt::VertexAttribute]>,
354}
355
356#[derive(Clone, Debug)]
358#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
359pub struct VertexState<'a> {
360 pub stage: ProgrammableStageDescriptor<'a>,
362 pub buffers: Cow<'a, [VertexBufferLayout<'a>]>,
364}
365
366#[derive(Clone, Debug)]
368#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
369pub struct FragmentState<'a> {
370 pub stage: ProgrammableStageDescriptor<'a>,
372 pub targets: Cow<'a, [Option<wgt::ColorTargetState>]>,
374}
375
376#[derive(Clone, Debug)]
378#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
379pub struct RenderPipelineDescriptor<'a> {
380 pub label: Label<'a>,
381 pub layout: Option<PipelineLayoutId>,
383 pub vertex: VertexState<'a>,
385 #[cfg_attr(feature = "serde", serde(default))]
387 pub primitive: wgt::PrimitiveState,
388 #[cfg_attr(feature = "serde", serde(default))]
390 pub depth_stencil: Option<wgt::DepthStencilState>,
391 #[cfg_attr(feature = "serde", serde(default))]
393 pub multisample: wgt::MultisampleState,
394 pub fragment: Option<FragmentState<'a>>,
396 pub multiview: Option<NonZeroU32>,
399}
400
401#[derive(Clone, Debug, Error)]
402#[non_exhaustive]
403pub enum ColorStateError {
404 #[error("Format {0:?} is not renderable")]
405 FormatNotRenderable(wgt::TextureFormat),
406 #[error("Format {0:?} is not blendable")]
407 FormatNotBlendable(wgt::TextureFormat),
408 #[error("Format {0:?} does not have a color aspect")]
409 FormatNotColor(wgt::TextureFormat),
410 #[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:?}.")]
411 InvalidSampleCount(u32, wgt::TextureFormat, Vec<u32>, Vec<u32>),
412 #[error("Output format {pipeline} is incompatible with the shader {shader}")]
413 IncompatibleFormat {
414 pipeline: validation::NumericType,
415 shader: validation::NumericType,
416 },
417 #[error("Blend factors for {0:?} must be `One`")]
418 InvalidMinMaxBlendFactors(wgt::BlendComponent),
419 #[error("Invalid write mask {0:?}")]
420 InvalidWriteMask(wgt::ColorWrites),
421}
422
423#[derive(Clone, Debug, Error)]
424#[non_exhaustive]
425pub enum DepthStencilStateError {
426 #[error("Format {0:?} is not renderable")]
427 FormatNotRenderable(wgt::TextureFormat),
428 #[error("Format {0:?} does not have a depth aspect, but depth test/write is enabled")]
429 FormatNotDepth(wgt::TextureFormat),
430 #[error("Format {0:?} does not have a stencil aspect, but stencil test/write is enabled")]
431 FormatNotStencil(wgt::TextureFormat),
432 #[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:?}.")]
433 InvalidSampleCount(u32, wgt::TextureFormat, Vec<u32>, Vec<u32>),
434}
435
436#[derive(Clone, Debug, Error)]
437#[non_exhaustive]
438pub enum CreateRenderPipelineError {
439 #[error(transparent)]
440 ColorAttachment(#[from] ColorAttachmentError),
441 #[error(transparent)]
442 Device(#[from] DeviceError),
443 #[error("Pipeline layout is invalid")]
444 InvalidLayout,
445 #[error("Unable to derive an implicit layout")]
446 Implicit(#[from] ImplicitLayoutError),
447 #[error("Color state [{0}] is invalid")]
448 ColorState(u8, #[source] ColorStateError),
449 #[error("Depth/stencil state is invalid")]
450 DepthStencilState(#[from] DepthStencilStateError),
451 #[error("Invalid sample count {0}")]
452 InvalidSampleCount(u32),
453 #[error("The number of vertex buffers {given} exceeds the limit {limit}")]
454 TooManyVertexBuffers { given: u32, limit: u32 },
455 #[error("The total number of vertex attributes {given} exceeds the limit {limit}")]
456 TooManyVertexAttributes { given: u32, limit: u32 },
457 #[error("Vertex buffer {index} stride {given} exceeds the limit {limit}")]
458 VertexStrideTooLarge { index: u32, given: u32, limit: u32 },
459 #[error("Vertex buffer {index} stride {stride} does not respect `VERTEX_STRIDE_ALIGNMENT`")]
460 UnalignedVertexStride {
461 index: u32,
462 stride: wgt::BufferAddress,
463 },
464 #[error("Vertex attribute at location {location} has invalid offset {offset}")]
465 InvalidVertexAttributeOffset {
466 location: wgt::ShaderLocation,
467 offset: wgt::BufferAddress,
468 },
469 #[error("Two or more vertex attributes were assigned to the same location in the shader: {0}")]
470 ShaderLocationClash(u32),
471 #[error("Strip index format was not set to None but to {strip_index_format:?} while using the non-strip topology {topology:?}")]
472 StripIndexFormatForNonStripTopology {
473 strip_index_format: Option<wgt::IndexFormat>,
474 topology: wgt::PrimitiveTopology,
475 },
476 #[error("Conservative Rasterization is only supported for wgt::PolygonMode::Fill")]
477 ConservativeRasterizationNonFillPolygonMode,
478 #[error(transparent)]
479 MissingFeatures(#[from] MissingFeatures),
480 #[error(transparent)]
481 MissingDownlevelFlags(#[from] MissingDownlevelFlags),
482 #[error("Error matching {stage:?} shader requirements against the pipeline")]
483 Stage {
484 stage: wgt::ShaderStages,
485 #[source]
486 error: validation::StageError,
487 },
488 #[error("Internal error in {stage:?} shader: {error}")]
489 Internal {
490 stage: wgt::ShaderStages,
491 error: String,
492 },
493 #[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.")]
494 UnalignedShader { group: u32, binding: u32, size: u64 },
495 #[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.")]
496 BlendFactorOnUnsupportedTarget {
497 factor: wgt::BlendFactor,
498 target: u32,
499 },
500 #[error("Pipeline expects the shader entry point to make use of dual-source blending.")]
501 PipelineExpectsShaderToUseDualSourceBlending,
502 #[error("Shader entry point expects the pipeline to make use of dual-source blending.")]
503 ShaderExpectsPipelineToUseDualSourceBlending,
504}
505
506bitflags::bitflags! {
507 #[repr(transparent)]
508 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
509 pub struct PipelineFlags: u32 {
510 const BLEND_CONSTANT = 1 << 0;
511 const STENCIL_REFERENCE = 1 << 1;
512 const WRITES_DEPTH = 1 << 2;
513 const WRITES_STENCIL = 1 << 3;
514 }
515}
516
517#[derive(Clone, Copy, Debug)]
519pub struct VertexStep {
520 pub stride: wgt::BufferAddress,
522
523 pub last_stride: wgt::BufferAddress,
525
526 pub mode: wgt::VertexStepMode,
528}
529
530impl Default for VertexStep {
531 fn default() -> Self {
532 Self {
533 stride: 0,
534 last_stride: 0,
535 mode: wgt::VertexStepMode::Vertex,
536 }
537 }
538}
539
540#[derive(Debug)]
541pub struct RenderPipeline<A: HalApi> {
542 pub(crate) raw: Option<A::RenderPipeline>,
543 pub(crate) device: Arc<Device<A>>,
544 pub(crate) layout: Arc<PipelineLayout<A>>,
545 pub(crate) _shader_modules:
546 ArrayVec<Arc<ShaderModule<A>>, { hal::MAX_CONCURRENT_SHADER_STAGES }>,
547 pub(crate) pass_context: RenderPassContext,
548 pub(crate) flags: PipelineFlags,
549 pub(crate) strip_index_format: Option<wgt::IndexFormat>,
550 pub(crate) vertex_steps: Vec<VertexStep>,
551 pub(crate) late_sized_buffer_groups: ArrayVec<LateSizedBufferGroup, { hal::MAX_BIND_GROUPS }>,
552 pub(crate) info: ResourceInfo<RenderPipeline<A>>,
553}
554
555impl<A: HalApi> Drop for RenderPipeline<A> {
556 fn drop(&mut self) {
557 if let Some(raw) = self.raw.take() {
558 resource_log!("Destroy raw RenderPipeline {:?}", self.info.label());
559
560 #[cfg(feature = "trace")]
561 if let Some(t) = self.device.trace.lock().as_mut() {
562 t.add(trace::Action::DestroyRenderPipeline(self.info.id()));
563 }
564
565 unsafe {
566 use hal::Device;
567 self.device.raw().destroy_render_pipeline(raw);
568 }
569 }
570 }
571}
572
573impl<A: HalApi> Resource for RenderPipeline<A> {
574 const TYPE: ResourceType = "RenderPipeline";
575
576 type Marker = crate::id::markers::RenderPipeline;
577
578 fn as_info(&self) -> &ResourceInfo<Self> {
579 &self.info
580 }
581
582 fn as_info_mut(&mut self) -> &mut ResourceInfo<Self> {
583 &mut self.info
584 }
585}
586
587impl<A: HalApi> RenderPipeline<A> {
588 pub(crate) fn raw(&self) -> &A::RenderPipeline {
589 self.raw.as_ref().unwrap()
590 }
591}