diff --git a/wgpu-core/src/device/resource.rs b/wgpu-core/src/device/resource.rs index a609edefca..79d70f424e 100644 --- a/wgpu-core/src/device/resource.rs +++ b/wgpu-core/src/device/resource.rs @@ -2901,18 +2901,8 @@ impl Device { let mut shader_expects_dual_source_blending = false; let mut pipeline_expects_dual_source_blending = false; for (i, vb_state) in desc.vertex.buffers.iter().enumerate() { - let mut last_stride = 0; - for attribute in vb_state.attributes.iter() { - last_stride = last_stride.max(attribute.offset + attribute.format.size()); - } - vertex_steps.push(pipeline::VertexStep { - stride: vb_state.array_stride, - last_stride, - mode: vb_state.step_mode, - }); - if vb_state.attributes.is_empty() { - continue; - } + // https://gpuweb.github.io/gpuweb/#abstract-opdef-validating-gpuvertexbufferlayout + if vb_state.array_stride > self.limits.max_vertex_buffer_array_stride as u64 { return Err(pipeline::CreateRenderPipelineError::VertexStrideTooLarge { index: i as u32, @@ -2926,6 +2916,54 @@ impl Device { stride: vb_state.array_stride, }); } + + let max_stride = if vb_state.array_stride == 0 { + self.limits.max_vertex_buffer_array_stride as u64 + } else { + vb_state.array_stride + }; + let mut last_stride = 0; + for attribute in vb_state.attributes.iter() { + let attribute_stride = attribute.offset + attribute.format.size(); + if attribute_stride > max_stride { + return Err( + pipeline::CreateRenderPipelineError::VertexAttributeStrideTooLarge { + location: attribute.shader_location, + given: attribute_stride as u32, + limit: max_stride as u32, + }, + ); + } + + let required_offset_alignment = attribute.format.size().min(4); + if attribute.offset % required_offset_alignment != 0 { + return Err( + pipeline::CreateRenderPipelineError::InvalidVertexAttributeOffset { + location: attribute.shader_location, + offset: attribute.offset, + }, + ); + } + + if attribute.shader_location >= self.limits.max_vertex_attributes { + return Err( + pipeline::CreateRenderPipelineError::TooManyVertexAttributes { + given: attribute.shader_location, + limit: self.limits.max_vertex_attributes, + }, + ); + } + + last_stride = last_stride.max(attribute_stride); + } + vertex_steps.push(pipeline::VertexStep { + stride: vb_state.array_stride, + last_stride, + mode: vb_state.step_mode, + }); + if vb_state.attributes.is_empty() { + continue; + } vertex_buffers.push(hal::VertexBufferLayout { array_stride: vb_state.array_stride, step_mode: vb_state.step_mode, diff --git a/wgpu-core/src/pipeline.rs b/wgpu-core/src/pipeline.rs index 6ba26f9ae2..32e2378029 100644 --- a/wgpu-core/src/pipeline.rs +++ b/wgpu-core/src/pipeline.rs @@ -475,6 +475,12 @@ pub enum CreateRenderPipelineError { TooManyVertexAttributes { given: u32, limit: u32 }, #[error("Vertex buffer {index} stride {given} exceeds the limit {limit}")] VertexStrideTooLarge { index: u32, given: u32, limit: u32 }, + #[error("Vertex attribute at location {location} stride {given} exceeds the limit {limit}")] + VertexAttributeStrideTooLarge { + location: wgt::ShaderLocation, + given: u32, + limit: u32, + }, #[error("Vertex buffer {index} stride {stride} does not respect `VERTEX_STRIDE_ALIGNMENT`")] UnalignedVertexStride { index: u32,