diff options
Diffstat (limited to 'third_party/rust/wgpu-core/src/pipeline.rs')
-rw-r--r-- | third_party/rust/wgpu-core/src/pipeline.rs | 561 |
1 files changed, 561 insertions, 0 deletions
diff --git a/third_party/rust/wgpu-core/src/pipeline.rs b/third_party/rust/wgpu-core/src/pipeline.rs new file mode 100644 index 0000000000..acc1b24b0c --- /dev/null +++ b/third_party/rust/wgpu-core/src/pipeline.rs @@ -0,0 +1,561 @@ +#[cfg(feature = "trace")] +use crate::device::trace; +use crate::{ + binding_model::{CreateBindGroupLayoutError, CreatePipelineLayoutError, PipelineLayout}, + command::ColorAttachmentError, + device::{Device, DeviceError, MissingDownlevelFlags, MissingFeatures, RenderPassContext}, + hal_api::HalApi, + id::{PipelineLayoutId, ShaderModuleId}, + resource::{Resource, ResourceInfo, ResourceType}, + resource_log, validation, Label, +}; +use arrayvec::ArrayVec; +use std::{borrow::Cow, error::Error, fmt, marker::PhantomData, num::NonZeroU32, sync::Arc}; +use thiserror::Error; + +/// Information about buffer bindings, which +/// is validated against the shader (and pipeline) +/// at draw time as opposed to initialization time. +#[derive(Debug)] +pub(crate) struct LateSizedBufferGroup { + // The order has to match `BindGroup::late_buffer_binding_sizes`. + pub(crate) shader_sizes: Vec<wgt::BufferAddress>, +} + +#[allow(clippy::large_enum_variant)] +pub enum ShaderModuleSource<'a> { + #[cfg(feature = "wgsl")] + Wgsl(Cow<'a, str>), + #[cfg(feature = "glsl")] + Glsl(Cow<'a, str>, naga::front::glsl::Options), + #[cfg(feature = "spirv")] + SpirV(Cow<'a, [u32]>, naga::front::spv::Options), + Naga(Cow<'static, naga::Module>), + /// Dummy variant because `Naga` doesn't have a lifetime and without enough active features it + /// could be the last one active. + #[doc(hidden)] + Dummy(PhantomData<&'a ()>), +} + +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct ShaderModuleDescriptor<'a> { + pub label: Label<'a>, + #[cfg_attr(feature = "serde", serde(default))] + pub shader_bound_checks: wgt::ShaderBoundChecks, +} + +#[derive(Debug)] +pub struct ShaderModule<A: HalApi> { + pub(crate) raw: Option<A::ShaderModule>, + pub(crate) device: Arc<Device<A>>, + pub(crate) interface: Option<validation::Interface>, + pub(crate) info: ResourceInfo<ShaderModule<A>>, + pub(crate) label: String, +} + +impl<A: HalApi> Drop for ShaderModule<A> { + fn drop(&mut self) { + if let Some(raw) = self.raw.take() { + resource_log!("Destroy raw ShaderModule {:?}", self.info.label()); + #[cfg(feature = "trace")] + if let Some(t) = self.device.trace.lock().as_mut() { + t.add(trace::Action::DestroyShaderModule(self.info.id())); + } + unsafe { + use hal::Device; + self.device.raw().destroy_shader_module(raw); + } + } + } +} + +impl<A: HalApi> Resource for ShaderModule<A> { + const TYPE: ResourceType = "ShaderModule"; + + type Marker = crate::id::markers::ShaderModule; + + fn as_info(&self) -> &ResourceInfo<Self> { + &self.info + } + + fn as_info_mut(&mut self) -> &mut ResourceInfo<Self> { + &mut self.info + } + + fn label(&self) -> String { + self.label.clone() + } +} + +impl<A: HalApi> ShaderModule<A> { + pub(crate) fn raw(&self) -> &A::ShaderModule { + self.raw.as_ref().unwrap() + } +} + +#[derive(Clone, Debug)] +pub struct ShaderError<E> { + pub source: String, + pub label: Option<String>, + pub inner: Box<E>, +} +#[cfg(feature = "wgsl")] +impl fmt::Display for ShaderError<naga::front::wgsl::ParseError> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let label = self.label.as_deref().unwrap_or_default(); + let string = self.inner.emit_to_string(&self.source); + write!(f, "\nShader '{label}' parsing {string}") + } +} +#[cfg(feature = "glsl")] +impl fmt::Display for ShaderError<naga::front::glsl::ParseError> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let label = self.label.as_deref().unwrap_or_default(); + let string = self.inner.emit_to_string(&self.source); + write!(f, "\nShader '{label}' parsing {string}") + } +} +#[cfg(feature = "spirv")] +impl fmt::Display for ShaderError<naga::front::spv::Error> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let label = self.label.as_deref().unwrap_or_default(); + let string = self.inner.emit_to_string(&self.source); + write!(f, "\nShader '{label}' parsing {string}") + } +} +impl fmt::Display for ShaderError<naga::WithSpan<naga::valid::ValidationError>> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use codespan_reporting::{ + diagnostic::{Diagnostic, Label}, + files::SimpleFile, + term, + }; + + let label = self.label.as_deref().unwrap_or_default(); + let files = SimpleFile::new(label, &self.source); + let config = term::Config::default(); + let mut writer = term::termcolor::NoColor::new(Vec::new()); + + let diagnostic = Diagnostic::error().with_labels( + self.inner + .spans() + .map(|&(span, ref desc)| { + Label::primary((), span.to_range().unwrap()).with_message(desc.to_owned()) + }) + .collect(), + ); + + term::emit(&mut writer, &config, &files, &diagnostic).expect("cannot write error"); + + write!( + f, + "\nShader validation {}", + String::from_utf8_lossy(&writer.into_inner()) + ) + } +} +impl<E> Error for ShaderError<E> +where + ShaderError<E>: fmt::Display, + E: Error + 'static, +{ + fn source(&self) -> Option<&(dyn Error + 'static)> { + Some(&self.inner) + } +} + +//Note: `Clone` would require `WithSpan: Clone`. +#[derive(Debug, Error)] +#[non_exhaustive] +pub enum CreateShaderModuleError { + #[cfg(feature = "wgsl")] + #[error(transparent)] + Parsing(#[from] ShaderError<naga::front::wgsl::ParseError>), + #[cfg(feature = "glsl")] + #[error(transparent)] + ParsingGlsl(#[from] ShaderError<naga::front::glsl::ParseError>), + #[cfg(feature = "spirv")] + #[error(transparent)] + ParsingSpirV(#[from] ShaderError<naga::front::spv::Error>), + #[error("Failed to generate the backend-specific code")] + Generation, + #[error(transparent)] + Device(#[from] DeviceError), + #[error(transparent)] + Validation(#[from] ShaderError<naga::WithSpan<naga::valid::ValidationError>>), + #[error(transparent)] + MissingFeatures(#[from] MissingFeatures), + #[error( + "Shader global {bind:?} uses a group index {group} that exceeds the max_bind_groups limit of {limit}." + )] + InvalidGroupIndex { + bind: naga::ResourceBinding, + group: u32, + limit: u32, + }, +} + +impl CreateShaderModuleError { + pub fn location(&self, source: &str) -> Option<naga::SourceLocation> { + match *self { + #[cfg(feature = "wgsl")] + CreateShaderModuleError::Parsing(ref err) => err.inner.location(source), + CreateShaderModuleError::Validation(ref err) => err.inner.location(source), + _ => None, + } + } +} + +/// Describes a programmable pipeline stage. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct ProgrammableStageDescriptor<'a> { + /// The compiled shader module for this stage. + pub module: ShaderModuleId, + /// The name of the entry point in the compiled shader. There must be a function with this name + /// in the shader. + pub entry_point: Cow<'a, str>, +} + +/// Number of implicit bind groups derived at pipeline creation. +pub type ImplicitBindGroupCount = u8; + +#[derive(Clone, Debug, Error)] +#[non_exhaustive] +pub enum ImplicitLayoutError { + #[error("Missing IDs for deriving {0} bind groups")] + MissingIds(ImplicitBindGroupCount), + #[error("Unable to reflect the shader {0:?} interface")] + ReflectionError(wgt::ShaderStages), + #[error(transparent)] + BindGroup(#[from] CreateBindGroupLayoutError), + #[error(transparent)] + Pipeline(#[from] CreatePipelineLayoutError), +} + +/// Describes a compute pipeline. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct ComputePipelineDescriptor<'a> { + pub label: Label<'a>, + /// The layout of bind groups for this pipeline. + pub layout: Option<PipelineLayoutId>, + /// The compiled compute stage and its entry point. + pub stage: ProgrammableStageDescriptor<'a>, +} + +#[derive(Clone, Debug, Error)] +#[non_exhaustive] +pub enum CreateComputePipelineError { + #[error(transparent)] + Device(#[from] DeviceError), + #[error("Pipeline layout is invalid")] + InvalidLayout, + #[error("Unable to derive an implicit layout")] + Implicit(#[from] ImplicitLayoutError), + #[error("Error matching shader requirements against the pipeline")] + Stage(#[from] validation::StageError), + #[error("Internal error: {0}")] + Internal(String), + #[error(transparent)] + MissingDownlevelFlags(#[from] MissingDownlevelFlags), +} + +#[derive(Debug)] +pub struct ComputePipeline<A: HalApi> { + pub(crate) raw: Option<A::ComputePipeline>, + pub(crate) layout: Arc<PipelineLayout<A>>, + pub(crate) device: Arc<Device<A>>, + pub(crate) _shader_module: Arc<ShaderModule<A>>, + pub(crate) late_sized_buffer_groups: ArrayVec<LateSizedBufferGroup, { hal::MAX_BIND_GROUPS }>, + pub(crate) info: ResourceInfo<ComputePipeline<A>>, +} + +impl<A: HalApi> Drop for ComputePipeline<A> { + fn drop(&mut self) { + if let Some(raw) = self.raw.take() { + resource_log!("Destroy raw ComputePipeline {:?}", self.info.label()); + + #[cfg(feature = "trace")] + if let Some(t) = self.device.trace.lock().as_mut() { + t.add(trace::Action::DestroyComputePipeline(self.info.id())); + } + + unsafe { + use hal::Device; + self.device.raw().destroy_compute_pipeline(raw); + } + } + } +} + +impl<A: HalApi> Resource for ComputePipeline<A> { + const TYPE: ResourceType = "ComputePipeline"; + + type Marker = crate::id::markers::ComputePipeline; + + fn as_info(&self) -> &ResourceInfo<Self> { + &self.info + } + + fn as_info_mut(&mut self) -> &mut ResourceInfo<Self> { + &mut self.info + } +} + +impl<A: HalApi> ComputePipeline<A> { + pub(crate) fn raw(&self) -> &A::ComputePipeline { + self.raw.as_ref().unwrap() + } +} + +/// Describes how the vertex buffer is interpreted. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] +pub struct VertexBufferLayout<'a> { + /// The stride, in bytes, between elements of this buffer. + pub array_stride: wgt::BufferAddress, + /// How often this vertex buffer is "stepped" forward. + pub step_mode: wgt::VertexStepMode, + /// The list of attributes which comprise a single vertex. + pub attributes: Cow<'a, [wgt::VertexAttribute]>, +} + +/// Describes the vertex process in a render pipeline. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct VertexState<'a> { + /// The compiled vertex stage and its entry point. + pub stage: ProgrammableStageDescriptor<'a>, + /// The format of any vertex buffers used with this pipeline. + pub buffers: Cow<'a, [VertexBufferLayout<'a>]>, +} + +/// Describes fragment processing in a render pipeline. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct FragmentState<'a> { + /// The compiled fragment stage and its entry point. + pub stage: ProgrammableStageDescriptor<'a>, + /// The effect of draw calls on the color aspect of the output target. + pub targets: Cow<'a, [Option<wgt::ColorTargetState>]>, +} + +/// Describes a render (graphics) pipeline. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct RenderPipelineDescriptor<'a> { + pub label: Label<'a>, + /// The layout of bind groups for this pipeline. + pub layout: Option<PipelineLayoutId>, + /// The vertex processing state for this pipeline. + pub vertex: VertexState<'a>, + /// The properties of the pipeline at the primitive assembly and rasterization level. + #[cfg_attr(feature = "serde", serde(default))] + pub primitive: wgt::PrimitiveState, + /// The effect of draw calls on the depth and stencil aspects of the output target, if any. + #[cfg_attr(feature = "serde", serde(default))] + pub depth_stencil: Option<wgt::DepthStencilState>, + /// The multi-sampling properties of the pipeline. + #[cfg_attr(feature = "serde", serde(default))] + pub multisample: wgt::MultisampleState, + /// The fragment processing state for this pipeline. + pub fragment: Option<FragmentState<'a>>, + /// If the pipeline will be used with a multiview render pass, this indicates how many array + /// layers the attachments will have. + pub multiview: Option<NonZeroU32>, +} + +#[derive(Clone, Debug, Error)] +#[non_exhaustive] +pub enum ColorStateError { + #[error("Format {0:?} is not renderable")] + FormatNotRenderable(wgt::TextureFormat), + #[error("Format {0:?} is not blendable")] + FormatNotBlendable(wgt::TextureFormat), + #[error("Format {0:?} does not have a color aspect")] + FormatNotColor(wgt::TextureFormat), + #[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:?}.")] + InvalidSampleCount(u32, wgt::TextureFormat, Vec<u32>, Vec<u32>), + #[error("Output format {pipeline} is incompatible with the shader {shader}")] + IncompatibleFormat { + pipeline: validation::NumericType, + shader: validation::NumericType, + }, + #[error("Blend factors for {0:?} must be `One`")] + InvalidMinMaxBlendFactors(wgt::BlendComponent), + #[error("Invalid write mask {0:?}")] + InvalidWriteMask(wgt::ColorWrites), +} + +#[derive(Clone, Debug, Error)] +#[non_exhaustive] +pub enum DepthStencilStateError { + #[error("Format {0:?} is not renderable")] + FormatNotRenderable(wgt::TextureFormat), + #[error("Format {0:?} does not have a depth aspect, but depth test/write is enabled")] + FormatNotDepth(wgt::TextureFormat), + #[error("Format {0:?} does not have a stencil aspect, but stencil test/write is enabled")] + FormatNotStencil(wgt::TextureFormat), + #[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:?}.")] + InvalidSampleCount(u32, wgt::TextureFormat, Vec<u32>, Vec<u32>), +} + +#[derive(Clone, Debug, Error)] +#[non_exhaustive] +pub enum CreateRenderPipelineError { + #[error(transparent)] + ColorAttachment(#[from] ColorAttachmentError), + #[error(transparent)] + Device(#[from] DeviceError), + #[error("Pipeline layout is invalid")] + InvalidLayout, + #[error("Unable to derive an implicit layout")] + Implicit(#[from] ImplicitLayoutError), + #[error("Color state [{0}] is invalid")] + ColorState(u8, #[source] ColorStateError), + #[error("Depth/stencil state is invalid")] + DepthStencilState(#[from] DepthStencilStateError), + #[error("Invalid sample count {0}")] + InvalidSampleCount(u32), + #[error("The number of vertex buffers {given} exceeds the limit {limit}")] + TooManyVertexBuffers { given: u32, limit: u32 }, + #[error("The total number of vertex attributes {given} exceeds the limit {limit}")] + TooManyVertexAttributes { given: u32, limit: u32 }, + #[error("Vertex buffer {index} stride {given} exceeds the limit {limit}")] + VertexStrideTooLarge { index: u32, given: u32, limit: u32 }, + #[error("Vertex buffer {index} stride {stride} does not respect `VERTEX_STRIDE_ALIGNMENT`")] + UnalignedVertexStride { + index: u32, + stride: wgt::BufferAddress, + }, + #[error("Vertex attribute at location {location} has invalid offset {offset}")] + InvalidVertexAttributeOffset { + location: wgt::ShaderLocation, + offset: wgt::BufferAddress, + }, + #[error("Two or more vertex attributes were assigned to the same location in the shader: {0}")] + ShaderLocationClash(u32), + #[error("Strip index format was not set to None but to {strip_index_format:?} while using the non-strip topology {topology:?}")] + StripIndexFormatForNonStripTopology { + strip_index_format: Option<wgt::IndexFormat>, + topology: wgt::PrimitiveTopology, + }, + #[error("Conservative Rasterization is only supported for wgt::PolygonMode::Fill")] + ConservativeRasterizationNonFillPolygonMode, + #[error(transparent)] + MissingFeatures(#[from] MissingFeatures), + #[error(transparent)] + MissingDownlevelFlags(#[from] MissingDownlevelFlags), + #[error("Error matching {stage:?} shader requirements against the pipeline")] + Stage { + stage: wgt::ShaderStages, + #[source] + error: validation::StageError, + }, + #[error("Internal error in {stage:?} shader: {error}")] + Internal { + stage: wgt::ShaderStages, + error: String, + }, + #[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.")] + UnalignedShader { group: u32, binding: u32, size: u64 }, + #[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.")] + BlendFactorOnUnsupportedTarget { + factor: wgt::BlendFactor, + target: u32, + }, + #[error("Pipeline expects the shader entry point to make use of dual-source blending.")] + PipelineExpectsShaderToUseDualSourceBlending, + #[error("Shader entry point expects the pipeline to make use of dual-source blending.")] + ShaderExpectsPipelineToUseDualSourceBlending, +} + +bitflags::bitflags! { + #[repr(transparent)] + #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] + pub struct PipelineFlags: u32 { + const BLEND_CONSTANT = 1 << 0; + const STENCIL_REFERENCE = 1 << 1; + const WRITES_DEPTH = 1 << 2; + const WRITES_STENCIL = 1 << 3; + } +} + +/// How a render pipeline will retrieve attributes from a particular vertex buffer. +#[derive(Clone, Copy, Debug)] +pub struct VertexStep { + /// The byte stride in the buffer between one attribute value and the next. + pub stride: wgt::BufferAddress, + + /// The byte size required to fit the last vertex in the stream. + pub last_stride: wgt::BufferAddress, + + /// Whether the buffer is indexed by vertex number or instance number. + pub mode: wgt::VertexStepMode, +} + +impl Default for VertexStep { + fn default() -> Self { + Self { + stride: 0, + last_stride: 0, + mode: wgt::VertexStepMode::Vertex, + } + } +} + +#[derive(Debug)] +pub struct RenderPipeline<A: HalApi> { + pub(crate) raw: Option<A::RenderPipeline>, + pub(crate) device: Arc<Device<A>>, + pub(crate) layout: Arc<PipelineLayout<A>>, + pub(crate) _shader_modules: + ArrayVec<Arc<ShaderModule<A>>, { hal::MAX_CONCURRENT_SHADER_STAGES }>, + pub(crate) pass_context: RenderPassContext, + pub(crate) flags: PipelineFlags, + pub(crate) strip_index_format: Option<wgt::IndexFormat>, + pub(crate) vertex_steps: Vec<VertexStep>, + pub(crate) late_sized_buffer_groups: ArrayVec<LateSizedBufferGroup, { hal::MAX_BIND_GROUPS }>, + pub(crate) info: ResourceInfo<RenderPipeline<A>>, +} + +impl<A: HalApi> Drop for RenderPipeline<A> { + fn drop(&mut self) { + if let Some(raw) = self.raw.take() { + resource_log!("Destroy raw RenderPipeline {:?}", self.info.label()); + + #[cfg(feature = "trace")] + if let Some(t) = self.device.trace.lock().as_mut() { + t.add(trace::Action::DestroyRenderPipeline(self.info.id())); + } + + unsafe { + use hal::Device; + self.device.raw().destroy_render_pipeline(raw); + } + } + } +} + +impl<A: HalApi> Resource for RenderPipeline<A> { + const TYPE: ResourceType = "RenderPipeline"; + + type Marker = crate::id::markers::RenderPipeline; + + fn as_info(&self) -> &ResourceInfo<Self> { + &self.info + } + + fn as_info_mut(&mut self) -> &mut ResourceInfo<Self> { + &mut self.info + } +} + +impl<A: HalApi> RenderPipeline<A> { + pub(crate) fn raw(&self) -> &A::RenderPipeline { + self.raw.as_ref().unwrap() + } +} |