wgpu_core/
pipeline.rs

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/// Information about buffer bindings, which
14/// is validated against the shader (and pipeline)
15/// at draw time as opposed to initialization time.
16#[derive(Debug)]
17pub(crate) struct LateSizedBufferGroup {
18    // The order has to match `BindGroup::late_buffer_binding_sizes`.
19    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    /// Dummy variant because `Naga` doesn't have a lifetime and without enough active features it
28    /// could be the last one active.
29    #[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//Note: `Clone` would require `WithSpan: Clone`.
122#[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/// Describes a programmable pipeline stage.
158#[derive(Clone, Debug)]
159#[cfg_attr(feature = "trace", derive(serde::Serialize))]
160#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
161pub struct ProgrammableStageDescriptor<'a> {
162    /// The compiled shader module for this stage.
163    pub module: ShaderModuleId,
164    /// The name of the entry point in the compiled shader. There must be a function with this name
165    /// in the shader.
166    pub entry_point: Cow<'a, str>,
167}
168
169/// Number of implicit bind groups derived at pipeline creation.
170pub 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/// Describes a compute pipeline.
186#[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    /// The layout of bind groups for this pipeline.
192    pub layout: Option<PipelineLayoutId>,
193    /// The compiled compute stage and its entry point.
194    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/// Describes how the vertex buffer is interpreted.
232#[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    /// The stride, in bytes, between elements of this buffer.
238    pub array_stride: wgt::BufferAddress,
239    /// How often this vertex buffer is "stepped" forward.
240    pub step_mode: wgt::VertexStepMode,
241    /// The list of attributes which comprise a single vertex.
242    pub attributes: Cow<'a, [wgt::VertexAttribute]>,
243}
244
245/// Describes the vertex process in a render pipeline.
246#[derive(Clone, Debug)]
247#[cfg_attr(feature = "trace", derive(serde::Serialize))]
248#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
249pub struct VertexState<'a> {
250    /// The compiled vertex stage and its entry point.
251    pub stage: ProgrammableStageDescriptor<'a>,
252    /// The format of any vertex buffers used with this pipeline.
253    pub buffers: Cow<'a, [VertexBufferLayout<'a>]>,
254}
255
256/// Describes fragment processing in a render pipeline.
257#[derive(Clone, Debug)]
258#[cfg_attr(feature = "trace", derive(serde::Serialize))]
259#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
260pub struct FragmentState<'a> {
261    /// The compiled fragment stage and its entry point.
262    pub stage: ProgrammableStageDescriptor<'a>,
263    /// The effect of draw calls on the color aspect of the output target.
264    pub targets: Cow<'a, [Option<wgt::ColorTargetState>]>,
265}
266
267/// Describes a render (graphics) pipeline.
268#[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    /// The layout of bind groups for this pipeline.
274    pub layout: Option<PipelineLayoutId>,
275    /// The vertex processing state for this pipeline.
276    pub vertex: VertexState<'a>,
277    /// The properties of the pipeline at the primitive assembly and rasterization level.
278    #[cfg_attr(any(feature = "replay", feature = "trace"), serde(default))]
279    pub primitive: wgt::PrimitiveState,
280    /// The effect of draw calls on the depth and stencil aspects of the output target, if any.
281    #[cfg_attr(any(feature = "replay", feature = "trace"), serde(default))]
282    pub depth_stencil: Option<wgt::DepthStencilState>,
283    /// The multi-sampling properties of the pipeline.
284    #[cfg_attr(any(feature = "replay", feature = "trace"), serde(default))]
285    pub multisample: wgt::MultisampleState,
286    /// The fragment processing state for this pipeline.
287    pub fragment: Option<FragmentState<'a>>,
288    /// If the pipeline will be used with a multiview render pass, this indicates how many array
289    /// layers the attachments will have.
290    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/// How a render pipeline will retrieve attributes from a particular vertex buffer.
401#[derive(Clone, Copy, Debug)]
402pub struct VertexStep {
403    /// The byte stride in the buffer between one attribute value and the next.
404    pub stride: wgt::BufferAddress,
405
406    /// Whether the buffer is indexed by vertex number or instance number.
407    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}