Add limits for textures sizes and vertex imputs

This commit is contained in:
Dzmitry Malyshau 2021-03-01 11:59:18 -05:00
parent 103e7763a3
commit 62ca24580d
7 changed files with 166 additions and 50 deletions

View File

@ -89,8 +89,14 @@ pub enum CreateBindGroupError {
SwapChainImage,
#[error("buffer offset {0} does not respect `BIND_BUFFER_ALIGNMENT`")]
UnalignedBufferOffset(wgt::BufferAddress),
#[error("uniform buffer binding range exceeds `max_uniform_buffer_binding_size` limit")]
UniformBufferRangeTooLarge,
#[error(
"buffer binding {binding} range {given} exceeds `max_*_buffer_binding_size` limit {limit}"
)]
BufferRangeTooLarge {
binding: u32,
given: u32,
limit: u32,
},
#[error("binding {binding} has a different type ({actual:?}) than the one in the layout ({expected:?})")]
WrongBindingType {
// Index of the binding

View File

@ -521,49 +521,60 @@ pub fn map_texture_dimension_size(
depth_or_array_layers,
}: wgt::Extent3d,
sample_size: u32,
limits: &wgt::Limits,
) -> Result<hal::image::Kind, resource::TextureDimensionError> {
use hal::image::Kind as H;
use resource::TextureDimensionError as Tde;
use resource::{TextureDimensionError as Tde, TextureErrorDimension as Ted};
use wgt::TextureDimension::*;
let zero_dim = if width == 0 {
Some(resource::TextureErrorDimension::X)
} else if height == 0 {
Some(resource::TextureErrorDimension::Y)
} else if depth_or_array_layers == 0 {
Some(resource::TextureErrorDimension::Z)
} else {
None
let layers = depth_or_array_layers.try_into().unwrap_or(!0);
let (kind, extent_limits, sample_limit) = match dimension {
D1 => (
H::D1(width, layers),
[
limits.max_texture_dimension_1d,
1,
limits.max_texture_array_layers,
],
1,
),
D2 => (
H::D2(width, height, layers, sample_size as u8),
[
limits.max_texture_dimension_2d,
limits.max_texture_dimension_2d,
limits.max_texture_array_layers,
],
32,
),
D3 => (
H::D3(width, height, depth_or_array_layers),
[
limits.max_texture_dimension_3d,
limits.max_texture_dimension_3d,
limits.max_texture_dimension_3d,
],
1,
),
};
if let Some(dim) = zero_dim {
return Err(resource::TextureDimensionError::Zero(dim));
for (&dim, (&given, &limit)) in [Ted::X, Ted::Y, Ted::Z].iter().zip(
[width, height, depth_or_array_layers]
.iter()
.zip(extent_limits.iter()),
) {
if given == 0 {
return Err(Tde::Zero(dim));
}
if given > limit {
return Err(Tde::LimitExceeded { dim, given, limit });
}
}
if sample_size == 0 || sample_size > sample_limit || !is_power_of_two(sample_size) {
return Err(Tde::InvalidSampleCount(sample_size));
}
Ok(match dimension {
D1 => {
if height != 1 {
return Err(Tde::InvalidHeight);
}
if sample_size != 1 {
return Err(Tde::InvalidSampleCount(sample_size));
}
let layers = depth_or_array_layers.try_into().unwrap_or(!0);
H::D1(width, layers)
}
D2 => {
if sample_size > 32 || !is_power_of_two(sample_size) {
return Err(Tde::InvalidSampleCount(sample_size));
}
let layers = depth_or_array_layers.try_into().unwrap_or(!0);
H::D2(width, height, layers, sample_size as u8)
}
D3 => {
if sample_size != 1 {
return Err(Tde::InvalidSampleCount(sample_size));
}
H::D3(width, height, depth_or_array_layers)
}
})
Ok(kind)
}
pub fn map_texture_view_dimension(dimension: wgt::TextureViewDimension) -> hal::image::ViewKind {

View File

@ -643,7 +643,12 @@ impl<B: GfxBackend> Device<B> {
));
}
let kind = conv::map_texture_dimension_size(desc.dimension, desc.size, desc.sample_count)?;
let kind = conv::map_texture_dimension_size(
desc.dimension,
desc.size,
desc.sample_count,
&self.limits,
)?;
let format = conv::map_texture_format(desc.format, self.private_features);
let aspects = format.surface_desc().aspects;
let usage = conv::map_texture_usage(desc.usage, aspects);
@ -1312,10 +1317,12 @@ impl<B: GfxBackend> Device<B> {
})
}
};
let (pub_usage, internal_use) = match binding_ty {
wgt::BufferBindingType::Uniform => {
(wgt::BufferUsage::UNIFORM, resource::BufferUse::UNIFORM)
}
let (pub_usage, internal_use, range_limit) = match binding_ty {
wgt::BufferBindingType::Uniform => (
wgt::BufferUsage::UNIFORM,
resource::BufferUse::UNIFORM,
self.limits.max_uniform_buffer_binding_size,
),
wgt::BufferBindingType::Storage { read_only } => (
wgt::BufferUsage::STORAGE,
if read_only {
@ -1323,6 +1330,7 @@ impl<B: GfxBackend> Device<B> {
} else {
resource::BufferUse::STORAGE_STORE
},
self.limits.max_storage_buffer_binding_size,
),
};
@ -1355,10 +1363,12 @@ impl<B: GfxBackend> Device<B> {
None => (buffer.size - bb.offset, buffer.size),
};
if binding_ty == wgt::BufferBindingType::Uniform
&& (self.limits.max_uniform_buffer_binding_size as u64) < bind_size
{
return Err(Error::UniformBufferRangeTooLarge);
if bind_size > range_limit as u64 {
return Err(Error::BufferRangeTooLarge {
binding,
given: bind_size as u32,
limit: range_limit,
});
}
// Record binding info for validating dynamic offsets
@ -2029,6 +2039,13 @@ impl<B: GfxBackend> Device<B> {
if vb_state.attributes.is_empty() {
continue;
}
if vb_state.array_stride > self.limits.max_vertex_buffer_array_stride as u64 {
return Err(pipeline::CreateRenderPipelineError::VertexStrideTooLarge {
index: i as u32,
given: vb_state.array_stride as u32,
limit: self.limits.max_vertex_buffer_array_stride,
});
}
if vb_state.array_stride % wgt::VERTEX_STRIDE_ALIGNMENT != 0 {
return Err(pipeline::CreateRenderPipelineError::UnalignedVertexStride {
index: i as u32,
@ -2084,6 +2101,21 @@ impl<B: GfxBackend> Device<B> {
}
}
if vertex_buffers.len() > self.limits.max_vertex_buffers as usize {
return Err(pipeline::CreateRenderPipelineError::TooManyVertexBuffers {
given: vertex_buffers.len() as u32,
limit: self.limits.max_vertex_buffers,
});
}
if attributes.len() > self.limits.max_vertex_attributes as usize {
return Err(
pipeline::CreateRenderPipelineError::TooManyVertexAttributes {
given: attributes.len() as u32,
limit: self.limits.max_vertex_attributes,
},
);
}
if desc.primitive.strip_index_format.is_some()
&& desc.primitive.topology != wgt::PrimitiveTopology::LineStrip
&& desc.primitive.topology != wgt::PrimitiveTopology::TriangleStrip

View File

@ -217,6 +217,20 @@ impl<B: GfxBackend> Adapter<B> {
// TODO: fix all gfx-hal backends to produce limits we care about, and remove .max
let desc_limits = &properties.limits.descriptor_limits;
let limits = wgt::Limits {
max_texture_dimension_1d: properties
.limits
.max_image_1d_size
.max(default_limits.max_texture_dimension_1d),
max_texture_dimension_2d: properties
.limits
.max_image_2d_size
.max(default_limits.max_texture_dimension_1d),
max_texture_dimension_3d: properties
.limits
.max_image_3d_size
.max(default_limits.max_texture_dimension_1d),
max_texture_array_layers: (properties.limits.max_image_array_layers as u32)
.max(default_limits.max_texture_array_layers),
max_bind_groups: (properties.limits.max_bound_descriptor_sets as u32)
.min(MAX_BIND_GROUPS as u32)
.max(default_limits.max_bind_groups),
@ -243,6 +257,15 @@ impl<B: GfxBackend> Adapter<B> {
.max(default_limits.max_uniform_buffers_per_shader_stage),
max_uniform_buffer_binding_size: (properties.limits.max_uniform_buffer_range as u32)
.max(default_limits.max_uniform_buffer_binding_size),
max_storage_buffer_binding_size: (properties.limits.max_storage_buffer_range as u32)
.max(default_limits.max_storage_buffer_binding_size),
max_vertex_buffers: (properties.limits.max_vertex_input_bindings as u32)
.max(default_limits.max_vertex_buffers),
max_vertex_attributes: (properties.limits.max_vertex_input_attributes as u32)
.max(default_limits.max_vertex_attributes),
max_vertex_buffer_array_stride: (properties.limits.max_vertex_input_binding_stride
as u32)
.max(default_limits.max_vertex_buffer_array_stride),
max_push_constant_size: (properties.limits.max_push_constants_size as u32)
.max(MIN_PUSH_CONSTANT_SIZE), // As an extension, the default is always 0, so define a separate minimum.
};

View File

@ -203,6 +203,12 @@ pub enum CreateRenderPipelineError {
IncompatibleOutputFormat { index: u8 },
#[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,

View File

@ -213,7 +213,7 @@ pub struct Texture<B: hal::Backend> {
pub(crate) life_guard: LifeGuard,
}
#[derive(Clone, Debug)]
#[derive(Clone, Copy, Debug)]
pub enum TextureErrorDimension {
X,
Y,
@ -224,8 +224,12 @@ pub enum TextureErrorDimension {
pub enum TextureDimensionError {
#[error("Dimension {0:?} is zero")]
Zero(TextureErrorDimension),
#[error("1D textures must have height set to 1")]
InvalidHeight,
#[error("Dimension {0:?} value {given} exceeds the limit of {limit}")]
LimitExceeded {
dim: TextureErrorDimension,
given: u32,
limit: u32,
},
#[error("sample count {0} is invalid")]
InvalidSampleCount(u32),
}

View File

@ -442,6 +442,20 @@ bitflags::bitflags! {
#[cfg_attr(feature = "trace", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct Limits {
/// Maximum allowed value for the `size.width` of a texture created with `TextureDimension::D1`.
/// Defaults to 8192. Higher is "better".
pub max_texture_dimension_1d: u32,
/// Maximum allowed value for the `size.width` and `size.height` of a texture created with `TextureDimension::D2`.
/// Defaults to 8192. Higher is "better".
pub max_texture_dimension_2d: u32,
/// Maximum allowed value for the `size.width`, `size.height`, and `size.depth_or_array_layers`
/// of a texture created with `TextureDimension::D3`.
/// Defaults to 2048. Higher is "better".
pub max_texture_dimension_3d: u32,
/// Maximum allowed value for the `size.depth_or_array_layers` of a texture created with
/// `TextureDimension::D1` or `TextureDimension::D2`.
/// Defaults to 2048. Higher is "better".
pub max_texture_array_layers: u32,
/// Amount of bind groups that can be attached to a pipeline at the same time. Defaults to 4. Higher is "better".
pub max_bind_groups: u32,
/// Amount of uniform buffer bindings that can be dynamic in a single pipeline. Defaults to 8. Higher is "better".
@ -460,6 +474,18 @@ pub struct Limits {
pub max_uniform_buffers_per_shader_stage: u32,
/// Maximum size in bytes of a binding to a uniform buffer. Defaults to 16384. Higher is "better".
pub max_uniform_buffer_binding_size: u32,
/// Maximum size in bytes of a binding to a storage buffer. Defaults to 128 MB. Higher is "better".
pub max_storage_buffer_binding_size: u32,
/// Maximum length of `VertexState::buffers` when creating a `RenderPipeline`.
/// Defaults to 8. Higher is "better".
pub max_vertex_buffers: u32,
/// Maximum length of `VertexBufferLayout::attributes`, summed over all `VertexState::buffers`,
/// when creating a `RenderPipeline`.
/// Defaults to 16. Higher is "better".
pub max_vertex_attributes: u32,
/// Maximum value for `VertexBufferLayout::array_stride` when creating a `RenderPipeline`.
/// Defaults to 2048. Higher is "better".
pub max_vertex_buffer_array_stride: u32,
/// Amount of storage available for push constants in bytes. Defaults to 0. Higher is "better".
/// Requesting more than 0 during device creation requires [`Features::PUSH_CONSTANTS`] to be enabled.
///
@ -475,6 +501,10 @@ pub struct Limits {
impl Default for Limits {
fn default() -> Self {
Self {
max_texture_dimension_1d: 8192,
max_texture_dimension_2d: 8192,
max_texture_dimension_3d: 2048,
max_texture_array_layers: 2048,
max_bind_groups: 4,
max_dynamic_uniform_buffers_per_pipeline_layout: 8,
max_dynamic_storage_buffers_per_pipeline_layout: 4,
@ -484,6 +514,10 @@ impl Default for Limits {
max_storage_textures_per_shader_stage: 4,
max_uniform_buffers_per_shader_stage: 12,
max_uniform_buffer_binding_size: 16384,
max_storage_buffer_binding_size: 128 << 20,
max_vertex_buffers: 8,
max_vertex_attributes: 16,
max_vertex_buffer_array_stride: 2048,
max_push_constant_size: 0,
}
}