1use crate::{
2 binding_model::{CreateBindGroupLayoutError, CreatePipelineLayoutError},
3 command::ColorAttachmentError,
4 device::{DeviceError, MissingDownlevelFlags, MissingFeatures, RenderPassContext},
5 id::{DeviceId, PipelineLayoutId, ShaderModuleId},
6 resource::Resource,
7 validation, Label, LifeGuard, Stored,
8};
9use arrayvec::ArrayVec;
10use std::{borrow::Cow, error::Error, fmt, marker::PhantomData, num::NonZeroU32};
11use thiserror::Error;
12
13#[derive(Debug)]
17pub(crate) struct LateSizedBufferGroup {
18 pub(crate) shader_sizes: Vec<wgt::BufferAddress>,
20}
21
22#[allow(clippy::large_enum_variant)]
23pub enum ShaderModuleSource<'a> {
24 #[cfg(feature = "wgsl")]
25 Wgsl(Cow<'a, str>),
26 Naga(Cow<'static, naga::Module>),
27 #[doc(hidden)]
30 Dummy(PhantomData<&'a ()>),
31}
32
33#[derive(Clone, Debug)]
34#[cfg_attr(feature = "trace", derive(serde::Serialize))]
35#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
36pub struct ShaderModuleDescriptor<'a> {
37 pub label: Label<'a>,
38 #[cfg_attr(feature = "serde", serde(default))]
39 pub shader_bound_checks: wgt::ShaderBoundChecks,
40}
41
42#[derive(Debug)]
43pub struct ShaderModule<A: hal::Api> {
44 pub(crate) raw: A::ShaderModule,
45 pub(crate) device_id: Stored<DeviceId>,
46 pub(crate) interface: Option<validation::Interface>,
47 #[cfg(debug_assertions)]
48 pub(crate) label: String,
49}
50
51impl<A: hal::Api> Resource for ShaderModule<A> {
52 const TYPE: &'static str = "ShaderModule";
53
54 fn life_guard(&self) -> &LifeGuard {
55 unreachable!()
56 }
57
58 fn label(&self) -> &str {
59 #[cfg(debug_assertions)]
60 return &self.label;
61 #[cfg(not(debug_assertions))]
62 return "";
63 }
64}
65
66#[derive(Clone, Debug)]
67pub struct ShaderError<E> {
68 pub source: String,
69 pub label: Option<String>,
70 pub inner: Box<E>,
71}
72#[cfg(feature = "wgsl")]
73impl fmt::Display for ShaderError<naga::front::wgsl::ParseError> {
74 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75 let label = self.label.as_deref().unwrap_or_default();
76 let string = self.inner.emit_to_string(&self.source);
77 write!(f, "\nShader '{label}' parsing {string}")
78 }
79}
80impl fmt::Display for ShaderError<naga::WithSpan<naga::valid::ValidationError>> {
81 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82 use codespan_reporting::{
83 diagnostic::{Diagnostic, Label},
84 files::SimpleFile,
85 term,
86 };
87
88 let label = self.label.as_deref().unwrap_or_default();
89 let files = SimpleFile::new(label, &self.source);
90 let config = term::Config::default();
91 let mut writer = term::termcolor::NoColor::new(Vec::new());
92
93 let diagnostic = Diagnostic::error().with_labels(
94 self.inner
95 .spans()
96 .map(|&(span, ref desc)| {
97 Label::primary((), span.to_range().unwrap()).with_message(desc.to_owned())
98 })
99 .collect(),
100 );
101
102 term::emit(&mut writer, &config, &files, &diagnostic).expect("cannot write error");
103
104 write!(
105 f,
106 "\nShader validation {}",
107 String::from_utf8_lossy(&writer.into_inner())
108 )
109 }
110}
111impl<E> Error for ShaderError<E>
112where
113 ShaderError<E>: fmt::Display,
114 E: Error + 'static,
115{
116 fn source(&self) -> Option<&(dyn Error + 'static)> {
117 Some(&self.inner)
118 }
119}
120
121#[derive(Debug, Error)]
123#[non_exhaustive]
124pub enum CreateShaderModuleError {
125 #[cfg(feature = "wgsl")]
126 #[error(transparent)]
127 Parsing(#[from] ShaderError<naga::front::wgsl::ParseError>),
128 #[error("Failed to generate the backend-specific code")]
129 Generation,
130 #[error(transparent)]
131 Device(#[from] DeviceError),
132 #[error(transparent)]
133 Validation(#[from] ShaderError<naga::WithSpan<naga::valid::ValidationError>>),
134 #[error(transparent)]
135 MissingFeatures(#[from] MissingFeatures),
136 #[error(
137 "Shader global {bind:?} uses a group index {group} that exceeds the max_bind_groups limit of {limit}."
138 )]
139 InvalidGroupIndex {
140 bind: naga::ResourceBinding,
141 group: u32,
142 limit: u32,
143 },
144}
145
146impl CreateShaderModuleError {
147 pub fn location(&self, source: &str) -> Option<naga::SourceLocation> {
148 match *self {
149 #[cfg(feature = "wgsl")]
150 CreateShaderModuleError::Parsing(ref err) => err.inner.location(source),
151 CreateShaderModuleError::Validation(ref err) => err.inner.location(source),
152 _ => None,
153 }
154 }
155}
156
157#[derive(Clone, Debug)]
159#[cfg_attr(feature = "trace", derive(serde::Serialize))]
160#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
161pub struct ProgrammableStageDescriptor<'a> {
162 pub module: ShaderModuleId,
164 pub entry_point: Cow<'a, str>,
167}
168
169pub type ImplicitBindGroupCount = u8;
171
172#[derive(Clone, Debug, Error)]
173#[non_exhaustive]
174pub enum ImplicitLayoutError {
175 #[error("Missing IDs for deriving {0} bind groups")]
176 MissingIds(ImplicitBindGroupCount),
177 #[error("Unable to reflect the shader {0:?} interface")]
178 ReflectionError(wgt::ShaderStages),
179 #[error(transparent)]
180 BindGroup(#[from] CreateBindGroupLayoutError),
181 #[error(transparent)]
182 Pipeline(#[from] CreatePipelineLayoutError),
183}
184
185#[derive(Clone, Debug)]
187#[cfg_attr(feature = "trace", derive(serde::Serialize))]
188#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
189pub struct ComputePipelineDescriptor<'a> {
190 pub label: Label<'a>,
191 pub layout: Option<PipelineLayoutId>,
193 pub stage: ProgrammableStageDescriptor<'a>,
195}
196
197#[derive(Clone, Debug, Error)]
198#[non_exhaustive]
199pub enum CreateComputePipelineError {
200 #[error(transparent)]
201 Device(#[from] DeviceError),
202 #[error("Pipeline layout is invalid")]
203 InvalidLayout,
204 #[error("Unable to derive an implicit layout")]
205 Implicit(#[from] ImplicitLayoutError),
206 #[error("Error matching shader requirements against the pipeline")]
207 Stage(#[from] validation::StageError),
208 #[error("Internal error: {0}")]
209 Internal(String),
210 #[error(transparent)]
211 MissingDownlevelFlags(#[from] MissingDownlevelFlags),
212}
213
214#[derive(Debug)]
215pub struct ComputePipeline<A: hal::Api> {
216 pub(crate) raw: A::ComputePipeline,
217 pub(crate) layout_id: Stored<PipelineLayoutId>,
218 pub(crate) device_id: Stored<DeviceId>,
219 pub(crate) late_sized_buffer_groups: ArrayVec<LateSizedBufferGroup, { hal::MAX_BIND_GROUPS }>,
220 pub(crate) life_guard: LifeGuard,
221}
222
223impl<A: hal::Api> Resource for ComputePipeline<A> {
224 const TYPE: &'static str = "ComputePipeline";
225
226 fn life_guard(&self) -> &LifeGuard {
227 &self.life_guard
228 }
229}
230
231#[derive(Clone, Debug)]
233#[cfg_attr(feature = "trace", derive(serde::Serialize))]
234#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
235#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
236pub struct VertexBufferLayout<'a> {
237 pub array_stride: wgt::BufferAddress,
239 pub step_mode: wgt::VertexStepMode,
241 pub attributes: Cow<'a, [wgt::VertexAttribute]>,
243}
244
245#[derive(Clone, Debug)]
247#[cfg_attr(feature = "trace", derive(serde::Serialize))]
248#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
249pub struct VertexState<'a> {
250 pub stage: ProgrammableStageDescriptor<'a>,
252 pub buffers: Cow<'a, [VertexBufferLayout<'a>]>,
254}
255
256#[derive(Clone, Debug)]
258#[cfg_attr(feature = "trace", derive(serde::Serialize))]
259#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
260pub struct FragmentState<'a> {
261 pub stage: ProgrammableStageDescriptor<'a>,
263 pub targets: Cow<'a, [Option<wgt::ColorTargetState>]>,
265}
266
267#[derive(Clone, Debug)]
269#[cfg_attr(feature = "trace", derive(serde::Serialize))]
270#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
271pub struct RenderPipelineDescriptor<'a> {
272 pub label: Label<'a>,
273 pub layout: Option<PipelineLayoutId>,
275 pub vertex: VertexState<'a>,
277 #[cfg_attr(any(feature = "replay", feature = "trace"), serde(default))]
279 pub primitive: wgt::PrimitiveState,
280 #[cfg_attr(any(feature = "replay", feature = "trace"), serde(default))]
282 pub depth_stencil: Option<wgt::DepthStencilState>,
283 #[cfg_attr(any(feature = "replay", feature = "trace"), serde(default))]
285 pub multisample: wgt::MultisampleState,
286 pub fragment: Option<FragmentState<'a>>,
288 pub multiview: Option<NonZeroU32>,
291}
292
293#[derive(Clone, Debug, Error)]
294#[non_exhaustive]
295pub enum ColorStateError {
296 #[error("Format {0:?} is not renderable")]
297 FormatNotRenderable(wgt::TextureFormat),
298 #[error("Format {0:?} is not blendable")]
299 FormatNotBlendable(wgt::TextureFormat),
300 #[error("Format {0:?} does not have a color aspect")]
301 FormatNotColor(wgt::TextureFormat),
302 #[error("Format {0:?} can't be multisampled")]
303 FormatNotMultisampled(wgt::TextureFormat),
304 #[error("Output format {pipeline} is incompatible with the shader {shader}")]
305 IncompatibleFormat {
306 pipeline: validation::NumericType,
307 shader: validation::NumericType,
308 },
309 #[error("Blend factors for {0:?} must be `One`")]
310 InvalidMinMaxBlendFactors(wgt::BlendComponent),
311 #[error("Invalid write mask {0:?}")]
312 InvalidWriteMask(wgt::ColorWrites),
313}
314
315#[derive(Clone, Debug, Error)]
316#[non_exhaustive]
317pub enum DepthStencilStateError {
318 #[error("Format {0:?} is not renderable")]
319 FormatNotRenderable(wgt::TextureFormat),
320 #[error("Format {0:?} does not have a depth aspect, but depth test/write is enabled")]
321 FormatNotDepth(wgt::TextureFormat),
322 #[error("Format {0:?} does not have a stencil aspect, but stencil test/write is enabled")]
323 FormatNotStencil(wgt::TextureFormat),
324 #[error("Format {0:?} can't be multisampled")]
325 FormatNotMultisampled(wgt::TextureFormat),
326}
327
328#[derive(Clone, Debug, Error)]
329#[non_exhaustive]
330pub enum CreateRenderPipelineError {
331 #[error(transparent)]
332 ColorAttachment(#[from] ColorAttachmentError),
333 #[error(transparent)]
334 Device(#[from] DeviceError),
335 #[error("Pipeline layout is invalid")]
336 InvalidLayout,
337 #[error("Unable to derive an implicit layout")]
338 Implicit(#[from] ImplicitLayoutError),
339 #[error("Color state [{0}] is invalid")]
340 ColorState(u8, #[source] ColorStateError),
341 #[error("Depth/stencil state is invalid")]
342 DepthStencilState(#[from] DepthStencilStateError),
343 #[error("Invalid sample count {0}")]
344 InvalidSampleCount(u32),
345 #[error("The number of vertex buffers {given} exceeds the limit {limit}")]
346 TooManyVertexBuffers { given: u32, limit: u32 },
347 #[error("The total number of vertex attributes {given} exceeds the limit {limit}")]
348 TooManyVertexAttributes { given: u32, limit: u32 },
349 #[error("Vertex buffer {index} stride {given} exceeds the limit {limit}")]
350 VertexStrideTooLarge { index: u32, given: u32, limit: u32 },
351 #[error("Vertex buffer {index} stride {stride} does not respect `VERTEX_STRIDE_ALIGNMENT`")]
352 UnalignedVertexStride {
353 index: u32,
354 stride: wgt::BufferAddress,
355 },
356 #[error("Vertex attribute at location {location} has invalid offset {offset}")]
357 InvalidVertexAttributeOffset {
358 location: wgt::ShaderLocation,
359 offset: wgt::BufferAddress,
360 },
361 #[error("Two or more vertex attributes were assigned to the same location in the shader: {0}")]
362 ShaderLocationClash(u32),
363 #[error("Strip index format was not set to None but to {strip_index_format:?} while using the non-strip topology {topology:?}")]
364 StripIndexFormatForNonStripTopology {
365 strip_index_format: Option<wgt::IndexFormat>,
366 topology: wgt::PrimitiveTopology,
367 },
368 #[error("Conservative Rasterization is only supported for wgt::PolygonMode::Fill")]
369 ConservativeRasterizationNonFillPolygonMode,
370 #[error(transparent)]
371 MissingFeatures(#[from] MissingFeatures),
372 #[error(transparent)]
373 MissingDownlevelFlags(#[from] MissingDownlevelFlags),
374 #[error("Error matching {stage:?} shader requirements against the pipeline")]
375 Stage {
376 stage: wgt::ShaderStages,
377 #[source]
378 error: validation::StageError,
379 },
380 #[error("Internal error in {stage:?} shader: {error}")]
381 Internal {
382 stage: wgt::ShaderStages,
383 error: String,
384 },
385 #[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.")]
386 UnalignedShader { group: u32, binding: u32, size: u64 },
387}
388
389bitflags::bitflags! {
390 #[repr(transparent)]
391 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
392 pub struct PipelineFlags: u32 {
393 const BLEND_CONSTANT = 1 << 0;
394 const STENCIL_REFERENCE = 1 << 1;
395 const WRITES_DEPTH = 1 << 2;
396 const WRITES_STENCIL = 1 << 3;
397 }
398}
399
400#[derive(Clone, Copy, Debug)]
402pub struct VertexStep {
403 pub stride: wgt::BufferAddress,
405
406 pub mode: wgt::VertexStepMode,
408}
409
410impl Default for VertexStep {
411 fn default() -> Self {
412 Self {
413 stride: 0,
414 mode: wgt::VertexStepMode::Vertex,
415 }
416 }
417}
418
419#[derive(Debug)]
420pub struct RenderPipeline<A: hal::Api> {
421 pub(crate) raw: A::RenderPipeline,
422 pub(crate) layout_id: Stored<PipelineLayoutId>,
423 pub(crate) device_id: Stored<DeviceId>,
424 pub(crate) pass_context: RenderPassContext,
425 pub(crate) flags: PipelineFlags,
426 pub(crate) strip_index_format: Option<wgt::IndexFormat>,
427 pub(crate) vertex_steps: Vec<VertexStep>,
428 pub(crate) late_sized_buffer_groups: ArrayVec<LateSizedBufferGroup, { hal::MAX_BIND_GROUPS }>,
429 pub(crate) life_guard: LifeGuard,
430}
431
432impl<A: hal::Api> Resource for RenderPipeline<A> {
433 const TYPE: &'static str = "RenderPipeline";
434
435 fn life_guard(&self) -> &LifeGuard {
436 &self.life_guard
437 }
438}