diff --git a/wgpu-core/src/command/compute.rs b/wgpu-core/src/command/compute.rs index a1399f8e75..ba19a2f0fc 100644 --- a/wgpu-core/src/command/compute.rs +++ b/wgpu-core/src/command/compute.rs @@ -23,7 +23,7 @@ use thiserror::Error; use wgt::{BufferAddress, BufferUsage, ShaderStage}; use crate::track::UseExtendError; -use std::{fmt, iter, str}; +use std::{fmt, iter, mem, str}; #[doc(hidden)] #[derive(Clone, Copy, Debug)] @@ -140,6 +140,12 @@ pub enum ComputePassErrorInner { InvalidQuerySet(id::QuerySetId), #[error("indirect buffer {0:?} is invalid or destroyed")] InvalidIndirectBuffer(id::BufferId), + #[error("indirect buffer uses bytes {offset}..{end_offset} which overruns indirect buffer of size {buffer_size}")] + IndirectBufferOverrun { + offset: u64, + end_offset: u64, + buffer_size: u64, + }, #[error(transparent)] ResourceUsageConflict(#[from] UsageConflict), #[error(transparent)] @@ -490,6 +496,17 @@ impl Global { .map_pass_err(scope)?; check_buffer_usage(indirect_buffer.usage, BufferUsage::INDIRECT) .map_pass_err(scope)?; + + let end_offset = offset + mem::size_of::() as u64; + if end_offset > indirect_buffer.size { + return Err(ComputePassErrorInner::IndirectBufferOverrun { + offset, + end_offset, + buffer_size: indirect_buffer.size, + }) + .map_pass_err(scope); + } + let &(ref buf_raw, _) = indirect_buffer .raw .as_ref() diff --git a/wgpu-core/src/command/render.rs b/wgpu-core/src/command/render.rs index bf3ff2e3b9..cf4292e7ec 100644 --- a/wgpu-core/src/command/render.rs +++ b/wgpu-core/src/command/render.rs @@ -45,6 +45,7 @@ use std::{ collections::hash_map::Entry, fmt, iter, marker::PhantomData, + mem, num::NonZeroU32, ops::Range, str, @@ -417,11 +418,10 @@ pub enum RenderPassErrorInner { InvalidValuesOffset, #[error("required device features not enabled: {0:?}")] MissingDeviceFeatures(wgt::Features), - #[error("indirect draw with offset {offset}{} uses bytes {begin_offset}..{end_offset} which overruns indirect buffer of size {buffer_size}", count.map_or_else(String::new, |v| format!(" and count {}", v)))] + #[error("indirect draw uses bytes {offset}..{end_offset} {} which overruns indirect buffer of size {buffer_size}", count.map_or_else(String::new, |v| format!("(using count {})", v)))] IndirectBufferOverrun { - offset: u64, count: Option, - begin_offset: u64, + offset: u64, end_offset: u64, buffer_size: u64, }, @@ -1549,9 +1549,9 @@ impl Global { state.is_ready().map_pass_err(scope)?; let stride = match indexed { - false => 16, - true => 20, - }; + false => mem::size_of::(), + true => mem::size_of::(), + } as u64; if count.is_some() { check_device_features( @@ -1577,13 +1577,11 @@ impl Global { let actual_count = count.map_or(1, |c| c.get()); - let begin_offset = offset; let end_offset = offset + stride * actual_count as u64; if end_offset > indirect_buffer.size { return Err(RenderPassErrorInner::IndirectBufferOverrun { - offset, count, - begin_offset, + offset, end_offset, buffer_size: indirect_buffer.size, }) @@ -1625,9 +1623,9 @@ impl Global { state.is_ready().map_pass_err(scope)?; let stride = match indexed { - false => 16, - true => 20, - }; + false => mem::size_of::(), + true => mem::size_of::(), + } as u64; check_device_features( device.features, @@ -1663,13 +1661,11 @@ impl Global { .ok_or(RenderCommandError::DestroyedBuffer(count_buffer_id)) .map_pass_err(scope)?; - let begin_offset = offset; let end_offset = offset + stride * max_count as u64; if end_offset > indirect_buffer.size { return Err(RenderPassErrorInner::IndirectBufferOverrun { - offset, count: None, - begin_offset, + offset, end_offset, buffer_size: indirect_buffer.size, }) diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs index a2f87eb6c2..61574e574f 100644 --- a/wgpu-types/src/lib.rs +++ b/wgpu-types/src/lib.rs @@ -2811,3 +2811,45 @@ bitflags::bitflags! { const COMPUTE_SHADER_INVOCATIONS = 0x10; } } + +/// Argument buffer layout for draw_indirect commands. +#[repr(C)] +#[derive(Clone, Debug)] +pub struct DrawIndirectArgs { + /// The number of vertices to draw. + pub vertex_count: u32, + /// The number of instances to draw. + pub instance_count: u32, + /// Offset into the vertex buffers, in vertices, to begin drawing from. + pub first_vertex: u32, + /// First instance to draw. + pub first_instance: u32, +} + +/// Argument buffer layout for draw_indexed_indirect commands. +#[repr(C)] +#[derive(Clone, Debug)] +pub struct DrawIndexedIndirectArgs { + /// The number of indices to draw. + pub index_count: u32, + /// The number of instances to draw. + pub instance_count: u32, + /// Offset into the index buffer, in indices, begin drawing from. + pub first_index: u32, + /// Added to each index value before indexing into the vertex buffers. + pub base_vertex: i32, + /// First instance to draw. + pub first_instance: u32, +} + +/// Argument buffer layout for dispatch_indirect commands. +#[repr(C)] +#[derive(Clone, Debug)] +pub struct DispatchIndirectArgs { + /// X dimension of the grid of workgroups to dispatch. + pub group_size_x: u32, + /// Y dimension of the grid of workgroups to dispatch. + pub group_size_y: u32, + /// Z dimension of the grid of workgroups to dispatch. + pub group_size_z: u32, +}