mirror of
https://github.com/gfx-rs/wgpu.git
synced 2025-12-08 21:26:17 +00:00
Restore unintentional support for zero-size buffers
This commit is contained in:
parent
ef428fcab8
commit
c0a580d6f0
@ -106,7 +106,7 @@ pub enum BindingError {
|
|||||||
binding_size: u64,
|
binding_size: u64,
|
||||||
buffer_size: u64,
|
buffer_size: u64,
|
||||||
},
|
},
|
||||||
#[error("Buffer {buffer}: Binding offset {offset} is greater than or equal to buffer size {buffer_size}")]
|
#[error("Buffer {buffer}: Binding offset {offset} is greater than buffer size {buffer_size}")]
|
||||||
BindingOffsetTooLarge {
|
BindingOffsetTooLarge {
|
||||||
buffer: ResourceErrorIdent,
|
buffer: ResourceErrorIdent,
|
||||||
offset: wgt::BufferAddress,
|
offset: wgt::BufferAddress,
|
||||||
|
|||||||
@ -93,6 +93,7 @@ use core::{
|
|||||||
use arrayvec::ArrayVec;
|
use arrayvec::ArrayVec;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use wgpu_hal::ShouldBeNonZeroExt;
|
||||||
use wgt::error::{ErrorType, WebGpuError};
|
use wgt::error::{ErrorType, WebGpuError};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -504,7 +505,7 @@ impl RenderBundleEncoder {
|
|||||||
buffer_id,
|
buffer_id,
|
||||||
index_format,
|
index_format,
|
||||||
offset,
|
offset,
|
||||||
size,
|
size: size.map(NonZeroU64::get),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -609,7 +610,7 @@ fn set_index_buffer(
|
|||||||
buffer_id: id::Id<id::markers::Buffer>,
|
buffer_id: id::Id<id::markers::Buffer>,
|
||||||
index_format: wgt::IndexFormat,
|
index_format: wgt::IndexFormat,
|
||||||
offset: u64,
|
offset: u64,
|
||||||
size: Option<NonZeroU64>,
|
size: Option<wgt::BufferSizeOrZero>,
|
||||||
) -> Result<(), RenderBundleErrorInner> {
|
) -> Result<(), RenderBundleErrorInner> {
|
||||||
let buffer = buffer_guard.get(buffer_id).get()?;
|
let buffer = buffer_guard.get(buffer_id).get()?;
|
||||||
|
|
||||||
@ -641,7 +642,7 @@ fn set_vertex_buffer(
|
|||||||
slot: u32,
|
slot: u32,
|
||||||
buffer_id: id::Id<id::markers::Buffer>,
|
buffer_id: id::Id<id::markers::Buffer>,
|
||||||
offset: u64,
|
offset: u64,
|
||||||
size: Option<NonZeroU64>,
|
size: Option<wgt::BufferSizeOrZero>,
|
||||||
) -> Result<(), RenderBundleErrorInner> {
|
) -> Result<(), RenderBundleErrorInner> {
|
||||||
let max_vertex_buffers = state.device.limits.max_vertex_buffers;
|
let max_vertex_buffers = state.device.limits.max_vertex_buffers;
|
||||||
if slot >= max_vertex_buffers {
|
if slot >= max_vertex_buffers {
|
||||||
@ -1166,11 +1167,8 @@ impl IndexState {
|
|||||||
.range
|
.range
|
||||||
.end
|
.end
|
||||||
.checked_sub(self.range.start)
|
.checked_sub(self.range.start)
|
||||||
.and_then(wgt::BufferSize::new);
|
.filter(|_| self.range.end <= self.buffer.size)
|
||||||
assert!(
|
.expect("index range must be contained in buffer");
|
||||||
self.range.end <= self.buffer.size && binding_size.is_some(),
|
|
||||||
"index buffer range must have non-zero size and be contained in buffer",
|
|
||||||
);
|
|
||||||
|
|
||||||
if self.is_dirty {
|
if self.is_dirty {
|
||||||
self.is_dirty = false;
|
self.is_dirty = false;
|
||||||
@ -1178,7 +1176,7 @@ impl IndexState {
|
|||||||
buffer: self.buffer.clone(),
|
buffer: self.buffer.clone(),
|
||||||
index_format: self.format,
|
index_format: self.format,
|
||||||
offset: self.range.start,
|
offset: self.range.start,
|
||||||
size: binding_size,
|
size: Some(binding_size),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@ -1221,16 +1219,12 @@ impl VertexState {
|
|||||||
///
|
///
|
||||||
/// `slot` is the index of the vertex buffer slot that `self` tracks.
|
/// `slot` is the index of the vertex buffer slot that `self` tracks.
|
||||||
fn flush(&mut self, slot: u32) -> Option<ArcRenderCommand> {
|
fn flush(&mut self, slot: u32) -> Option<ArcRenderCommand> {
|
||||||
// This was all checked before, but let's check again just in case.
|
|
||||||
let binding_size = self
|
let binding_size = self
|
||||||
.range
|
.range
|
||||||
.end
|
.end
|
||||||
.checked_sub(self.range.start)
|
.checked_sub(self.range.start)
|
||||||
.and_then(wgt::BufferSize::new);
|
.filter(|_| self.range.end <= self.buffer.size)
|
||||||
assert!(
|
.expect("vertex range must be contained in buffer");
|
||||||
self.range.end <= self.buffer.size && binding_size.is_some(),
|
|
||||||
"vertex buffer range must have non-zero size and be contained in buffer",
|
|
||||||
);
|
|
||||||
|
|
||||||
if self.is_dirty {
|
if self.is_dirty {
|
||||||
self.is_dirty = false;
|
self.is_dirty = false;
|
||||||
@ -1238,7 +1232,7 @@ impl VertexState {
|
|||||||
slot,
|
slot,
|
||||||
buffer: self.buffer.clone(),
|
buffer: self.buffer.clone(),
|
||||||
offset: self.range.start,
|
offset: self.range.start,
|
||||||
size: binding_size,
|
size: Some(binding_size),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@ -1602,7 +1596,7 @@ where
|
|||||||
pub mod bundle_ffi {
|
pub mod bundle_ffi {
|
||||||
use super::{RenderBundleEncoder, RenderCommand};
|
use super::{RenderBundleEncoder, RenderCommand};
|
||||||
use crate::{id, RawString};
|
use crate::{id, RawString};
|
||||||
use core::{convert::TryInto, slice};
|
use core::{convert::TryInto, num::NonZeroU64, slice};
|
||||||
use wgt::{BufferAddress, BufferSize, DynamicOffset, IndexFormat};
|
use wgt::{BufferAddress, BufferSize, DynamicOffset, IndexFormat};
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
@ -1661,7 +1655,7 @@ pub mod bundle_ffi {
|
|||||||
slot,
|
slot,
|
||||||
buffer_id,
|
buffer_id,
|
||||||
offset,
|
offset,
|
||||||
size,
|
size: size.map(NonZeroU64::get),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,12 +1,17 @@
|
|||||||
use alloc::{borrow::Cow, sync::Arc, vec::Vec};
|
use alloc::{borrow::Cow, sync::Arc, vec::Vec};
|
||||||
use core::{fmt, num::NonZeroU32, str};
|
use core::{
|
||||||
|
fmt,
|
||||||
|
num::{NonZeroU32, NonZeroU64},
|
||||||
|
str,
|
||||||
|
};
|
||||||
|
use hal::ShouldBeNonZeroExt;
|
||||||
|
|
||||||
use arrayvec::ArrayVec;
|
use arrayvec::ArrayVec;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use wgt::{
|
use wgt::{
|
||||||
error::{ErrorType, WebGpuError},
|
error::{ErrorType, WebGpuError},
|
||||||
BufferAddress, BufferSize, BufferUsages, Color, DynamicOffset, IndexFormat, ShaderStages,
|
BufferAddress, BufferSize, BufferSizeOrZero, BufferUsages, Color, DynamicOffset, IndexFormat,
|
||||||
TextureSelector, TextureUsages, TextureViewDimension, VertexStepMode,
|
ShaderStages, TextureSelector, TextureUsages, TextureViewDimension, VertexStepMode,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::command::{
|
use crate::command::{
|
||||||
@ -2333,7 +2338,7 @@ fn set_index_buffer(
|
|||||||
buffer: Arc<crate::resource::Buffer>,
|
buffer: Arc<crate::resource::Buffer>,
|
||||||
index_format: IndexFormat,
|
index_format: IndexFormat,
|
||||||
offset: u64,
|
offset: u64,
|
||||||
size: Option<BufferSize>,
|
size: Option<BufferSizeOrZero>,
|
||||||
) -> Result<(), RenderPassErrorInner> {
|
) -> Result<(), RenderPassErrorInner> {
|
||||||
api_log!("RenderPass::set_index_buffer {}", buffer.error_ident());
|
api_log!("RenderPass::set_index_buffer {}", buffer.error_ident());
|
||||||
|
|
||||||
@ -2373,7 +2378,7 @@ fn set_vertex_buffer(
|
|||||||
slot: u32,
|
slot: u32,
|
||||||
buffer: Arc<crate::resource::Buffer>,
|
buffer: Arc<crate::resource::Buffer>,
|
||||||
offset: u64,
|
offset: u64,
|
||||||
size: Option<BufferSize>,
|
size: Option<BufferSizeOrZero>,
|
||||||
) -> Result<(), RenderPassErrorInner> {
|
) -> Result<(), RenderPassErrorInner> {
|
||||||
api_log!(
|
api_log!(
|
||||||
"RenderPass::set_vertex_buffer {slot} {}",
|
"RenderPass::set_vertex_buffer {slot} {}",
|
||||||
@ -3084,7 +3089,7 @@ impl Global {
|
|||||||
buffer: pass_try!(base, scope, self.resolve_render_pass_buffer_id(buffer_id)),
|
buffer: pass_try!(base, scope, self.resolve_render_pass_buffer_id(buffer_id)),
|
||||||
index_format,
|
index_format,
|
||||||
offset,
|
offset,
|
||||||
size,
|
size: size.map(NonZeroU64::get),
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -3105,7 +3110,7 @@ impl Global {
|
|||||||
slot,
|
slot,
|
||||||
buffer: pass_try!(base, scope, self.resolve_render_pass_buffer_id(buffer_id)),
|
buffer: pass_try!(base, scope, self.resolve_render_pass_buffer_id(buffer_id)),
|
||||||
offset,
|
offset,
|
||||||
size,
|
size: size.map(NonZeroU64::get),
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
use alloc::sync::Arc;
|
use alloc::sync::Arc;
|
||||||
|
|
||||||
use wgt::{BufferAddress, BufferSize, Color};
|
use wgt::{BufferAddress, BufferSizeOrZero, Color};
|
||||||
|
|
||||||
use super::{Rect, RenderBundle};
|
use super::{Rect, RenderBundle};
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -24,13 +24,13 @@ pub enum RenderCommand {
|
|||||||
buffer_id: id::BufferId,
|
buffer_id: id::BufferId,
|
||||||
index_format: wgt::IndexFormat,
|
index_format: wgt::IndexFormat,
|
||||||
offset: BufferAddress,
|
offset: BufferAddress,
|
||||||
size: Option<BufferSize>,
|
size: Option<BufferSizeOrZero>,
|
||||||
},
|
},
|
||||||
SetVertexBuffer {
|
SetVertexBuffer {
|
||||||
slot: u32,
|
slot: u32,
|
||||||
buffer_id: id::BufferId,
|
buffer_id: id::BufferId,
|
||||||
offset: BufferAddress,
|
offset: BufferAddress,
|
||||||
size: Option<BufferSize>,
|
size: Option<BufferSizeOrZero>,
|
||||||
},
|
},
|
||||||
SetBlendConstant(Color),
|
SetBlendConstant(Color),
|
||||||
SetStencilReference(u32),
|
SetStencilReference(u32),
|
||||||
@ -418,21 +418,18 @@ pub enum ArcRenderCommand {
|
|||||||
offset: BufferAddress,
|
offset: BufferAddress,
|
||||||
|
|
||||||
// For a render pass, this reflects the argument passed by the
|
// For a render pass, this reflects the argument passed by the
|
||||||
// application, which may be `None`. For a render bundle, this reflects
|
// application, which may be `None`. For a finished render bundle, this
|
||||||
// the validated size of the binding, and will be populated even in the
|
// reflects the validated size of the binding, and will be populated
|
||||||
// case that the application omitted the size.
|
// even in the case that the application omitted the size.
|
||||||
size: Option<BufferSize>,
|
size: Option<BufferSizeOrZero>,
|
||||||
},
|
},
|
||||||
SetVertexBuffer {
|
SetVertexBuffer {
|
||||||
slot: u32,
|
slot: u32,
|
||||||
buffer: Arc<Buffer>,
|
buffer: Arc<Buffer>,
|
||||||
offset: BufferAddress,
|
offset: BufferAddress,
|
||||||
|
|
||||||
// For a render pass, this reflects the argument passed by the
|
// See comment in `SetIndexBuffer`.
|
||||||
// application, which may be `None`. For a render bundle, this reflects
|
size: Option<BufferSizeOrZero>,
|
||||||
// the validated size of the binding, and will be populated even in the
|
|
||||||
// case that the application omitted the size.
|
|
||||||
size: Option<BufferSize>,
|
|
||||||
},
|
},
|
||||||
SetBlendConstant(Color),
|
SetBlendConstant(Color),
|
||||||
SetStencilReference(u32),
|
SetStencilReference(u32),
|
||||||
|
|||||||
@ -8,9 +8,10 @@ use alloc::{
|
|||||||
use core::{
|
use core::{
|
||||||
fmt,
|
fmt,
|
||||||
mem::{self, ManuallyDrop},
|
mem::{self, ManuallyDrop},
|
||||||
num::NonZeroU32,
|
num::{NonZeroU32, NonZeroU64},
|
||||||
sync::atomic::{AtomicBool, Ordering},
|
sync::atomic::{AtomicBool, Ordering},
|
||||||
};
|
};
|
||||||
|
use hal::ShouldBeNonZeroExt;
|
||||||
|
|
||||||
use arrayvec::ArrayVec;
|
use arrayvec::ArrayVec;
|
||||||
use bitflags::Flags;
|
use bitflags::Flags;
|
||||||
@ -2197,7 +2198,7 @@ impl Device {
|
|||||||
|
|
||||||
buffer.check_usage(pub_usage)?;
|
buffer.check_usage(pub_usage)?;
|
||||||
|
|
||||||
let bb = buffer.binding(bb.offset, bb.size, snatch_guard)?;
|
let bb = buffer.binding(bb.offset, bb.size.map(NonZeroU64::get), snatch_guard)?;
|
||||||
let bind_size = bb.size.get();
|
let bind_size = bb.size.get();
|
||||||
|
|
||||||
if bind_size > range_limit as u64 {
|
if bind_size > range_limit as u64 {
|
||||||
|
|||||||
@ -490,35 +490,32 @@ impl Buffer {
|
|||||||
/// If `size` is `None`, then the remainder of the buffer starting from
|
/// If `size` is `None`, then the remainder of the buffer starting from
|
||||||
/// `offset` is used.
|
/// `offset` is used.
|
||||||
///
|
///
|
||||||
/// If the binding would overflow the buffer or is empty (see
|
/// If the binding would overflow the buffer, then an error is returned.
|
||||||
/// [`hal::BufferBinding`]), then an error is returned.
|
///
|
||||||
|
/// Zero-size bindings are permitted here for historical reasons. Although
|
||||||
|
/// zero-size bindings are permitted by WebGPU, they are not permitted by
|
||||||
|
/// some backends. See [`Buffer::binding`] and
|
||||||
|
/// [#3170](https://github.com/gfx-rs/wgpu/issues/3170).
|
||||||
pub fn resolve_binding_size(
|
pub fn resolve_binding_size(
|
||||||
&self,
|
&self,
|
||||||
offset: wgt::BufferAddress,
|
offset: wgt::BufferAddress,
|
||||||
binding_size: Option<wgt::BufferSize>,
|
binding_size: Option<wgt::BufferSizeOrZero>,
|
||||||
) -> Result<wgt::BufferSize, BindingError> {
|
) -> Result<wgt::BufferSizeOrZero, BindingError> {
|
||||||
let buffer_size = self.size;
|
let buffer_size = self.size;
|
||||||
|
|
||||||
match binding_size {
|
match binding_size {
|
||||||
Some(binding_size) => {
|
Some(binding_size) => match offset.checked_add(binding_size) {
|
||||||
match offset.checked_add(binding_size.get()) {
|
Some(end) if end <= buffer_size => Ok(binding_size),
|
||||||
// `binding_size` is not zero which means `end == buffer_size` is ok.
|
_ => Err(BindingError::BindingRangeTooLarge {
|
||||||
Some(end) if end <= buffer_size => Ok(binding_size),
|
buffer: self.error_ident(),
|
||||||
_ => Err(BindingError::BindingRangeTooLarge {
|
offset,
|
||||||
buffer: self.error_ident(),
|
binding_size,
|
||||||
offset,
|
buffer_size,
|
||||||
binding_size: binding_size.get(),
|
}),
|
||||||
buffer_size,
|
},
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => {
|
None => {
|
||||||
// We require that `buffer_size - offset` converts to
|
|
||||||
// `BufferSize` (`NonZeroU64`) because bindings must not be
|
|
||||||
// empty.
|
|
||||||
buffer_size
|
buffer_size
|
||||||
.checked_sub(offset)
|
.checked_sub(offset)
|
||||||
.and_then(wgt::BufferSize::new)
|
|
||||||
.ok_or_else(|| BindingError::BindingOffsetTooLarge {
|
.ok_or_else(|| BindingError::BindingOffsetTooLarge {
|
||||||
buffer: self.error_ident(),
|
buffer: self.error_ident(),
|
||||||
offset,
|
offset,
|
||||||
@ -534,12 +531,20 @@ impl Buffer {
|
|||||||
/// If `size` is `None`, then the remainder of the buffer starting from
|
/// If `size` is `None`, then the remainder of the buffer starting from
|
||||||
/// `offset` is used.
|
/// `offset` is used.
|
||||||
///
|
///
|
||||||
/// If the binding would overflow the buffer or is empty (see
|
/// If the binding would overflow the buffer, then an error is returned.
|
||||||
/// [`hal::BufferBinding`]), then an error is returned.
|
///
|
||||||
|
/// Zero-size bindings are permitted here for historical reasons. Although
|
||||||
|
/// zero-size bindings are permitted by WebGPU, they are not permitted by
|
||||||
|
/// some backends. Previous documentation for `hal::BufferBinding`
|
||||||
|
/// disallowed zero-size bindings, but this restriction was not honored
|
||||||
|
/// elsewhere in the code. Zero-size bindings need to be quashed or remapped
|
||||||
|
/// to a non-zero size, either universally in wgpu-core, or in specific
|
||||||
|
/// backends that do not support them. See
|
||||||
|
/// [#3170](https://github.com/gfx-rs/wgpu/issues/3170).
|
||||||
pub fn binding<'a>(
|
pub fn binding<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
offset: wgt::BufferAddress,
|
offset: wgt::BufferAddress,
|
||||||
binding_size: Option<wgt::BufferSize>,
|
binding_size: Option<wgt::BufferSizeOrZero>,
|
||||||
snatch_guard: &'a SnatchGuard,
|
snatch_guard: &'a SnatchGuard,
|
||||||
) -> Result<hal::BufferBinding<'a, dyn hal::DynBuffer>, BindingError> {
|
) -> Result<hal::BufferBinding<'a, dyn hal::DynBuffer>, BindingError> {
|
||||||
let buf_raw = self.try_raw(snatch_guard)?;
|
let buf_raw = self.try_raw(snatch_guard)?;
|
||||||
|
|||||||
@ -447,11 +447,7 @@ impl<A: hal::Api> Example<A> {
|
|||||||
let global_group = {
|
let global_group = {
|
||||||
let global_buffer_binding = unsafe {
|
let global_buffer_binding = unsafe {
|
||||||
// SAFETY: This is the same size that was specified for buffer creation.
|
// SAFETY: This is the same size that was specified for buffer creation.
|
||||||
hal::BufferBinding::new_unchecked(
|
hal::BufferBinding::new_unchecked(&global_buffer, 0, global_buffer_desc.size)
|
||||||
&global_buffer,
|
|
||||||
0,
|
|
||||||
global_buffer_desc.size.try_into().unwrap(),
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
let texture_binding = hal::TextureBinding {
|
let texture_binding = hal::TextureBinding {
|
||||||
view: &texture_view,
|
view: &texture_view,
|
||||||
|
|||||||
@ -1136,7 +1136,7 @@ impl crate::CommandEncoder for super::CommandEncoder {
|
|||||||
) {
|
) {
|
||||||
let ibv = Direct3D12::D3D12_INDEX_BUFFER_VIEW {
|
let ibv = Direct3D12::D3D12_INDEX_BUFFER_VIEW {
|
||||||
BufferLocation: binding.resolve_address(),
|
BufferLocation: binding.resolve_address(),
|
||||||
SizeInBytes: binding.resolve_size() as u32,
|
SizeInBytes: binding.size.try_into().unwrap(),
|
||||||
Format: auxil::dxgi::conv::map_index_format(format),
|
Format: auxil::dxgi::conv::map_index_format(format),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1149,7 +1149,7 @@ impl crate::CommandEncoder for super::CommandEncoder {
|
|||||||
) {
|
) {
|
||||||
let vb = &mut self.pass.vertex_buffers[index as usize];
|
let vb = &mut self.pass.vertex_buffers[index as usize];
|
||||||
vb.BufferLocation = binding.resolve_address();
|
vb.BufferLocation = binding.resolve_address();
|
||||||
vb.SizeInBytes = binding.resolve_size() as u32;
|
vb.SizeInBytes = binding.size.try_into().unwrap();
|
||||||
self.pass.dirty_vertex_buffers |= 1 << index;
|
self.pass.dirty_vertex_buffers |= 1 << index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1442,7 +1442,7 @@ impl crate::Device for super::Device {
|
|||||||
let end = start + entry.count as usize;
|
let end = start + entry.count as usize;
|
||||||
for data in &desc.buffers[start..end] {
|
for data in &desc.buffers[start..end] {
|
||||||
let gpu_address = data.resolve_address();
|
let gpu_address = data.resolve_address();
|
||||||
let mut size = data.resolve_size() as u32;
|
let mut size = data.size.try_into().unwrap();
|
||||||
|
|
||||||
if has_dynamic_offset {
|
if has_dynamic_offset {
|
||||||
match ty {
|
match ty {
|
||||||
|
|||||||
@ -865,13 +865,6 @@ unsafe impl Sync for Buffer {}
|
|||||||
impl crate::DynBuffer for Buffer {}
|
impl crate::DynBuffer for Buffer {}
|
||||||
|
|
||||||
impl crate::BufferBinding<'_, Buffer> {
|
impl crate::BufferBinding<'_, Buffer> {
|
||||||
fn resolve_size(&self) -> wgt::BufferAddress {
|
|
||||||
match self.size {
|
|
||||||
Some(size) => size.get(),
|
|
||||||
None => self.buffer.size - self.offset,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Return GPU handle directly?
|
// TODO: Return GPU handle directly?
|
||||||
fn resolve_address(&self) -> wgt::BufferAddress {
|
fn resolve_address(&self) -> wgt::BufferAddress {
|
||||||
(unsafe { self.buffer.resource.GetGPUVirtualAddress() }) + self.offset
|
(unsafe { self.buffer.resource.GetGPUVirtualAddress() }) + self.offset
|
||||||
|
|||||||
@ -1261,10 +1261,11 @@ impl crate::Device for super::Device {
|
|||||||
let binding = match layout.ty {
|
let binding = match layout.ty {
|
||||||
wgt::BindingType::Buffer { .. } => {
|
wgt::BindingType::Buffer { .. } => {
|
||||||
let bb = &desc.buffers[entry.resource_index as usize];
|
let bb = &desc.buffers[entry.resource_index as usize];
|
||||||
|
assert!(bb.size != 0, "zero-size bindings are not supported");
|
||||||
super::RawBinding::Buffer {
|
super::RawBinding::Buffer {
|
||||||
raw: bb.buffer.raw.unwrap(),
|
raw: bb.buffer.raw.unwrap(),
|
||||||
offset: bb.offset.try_into().unwrap(),
|
offset: bb.offset.try_into().unwrap(),
|
||||||
size: bb.size.get().try_into().unwrap(),
|
size: bb.size.try_into().unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
wgt::BindingType::Sampler { .. } => {
|
wgt::BindingType::Sampler { .. } => {
|
||||||
|
|||||||
@ -297,7 +297,7 @@ use core::{
|
|||||||
borrow::Borrow,
|
borrow::Borrow,
|
||||||
error::Error,
|
error::Error,
|
||||||
fmt,
|
fmt,
|
||||||
num::NonZeroU32,
|
num::{NonZeroU32, NonZeroU64},
|
||||||
ops::{Range, RangeInclusive},
|
ops::{Range, RangeInclusive},
|
||||||
ptr::NonNull,
|
ptr::NonNull,
|
||||||
};
|
};
|
||||||
@ -1979,7 +1979,7 @@ pub struct PipelineLayoutDescriptor<'a, B: DynBindGroupLayout + ?Sized> {
|
|||||||
///
|
///
|
||||||
/// `wgpu_hal` guarantees that shaders compiled with
|
/// `wgpu_hal` guarantees that shaders compiled with
|
||||||
/// [`ShaderModuleDescriptor::runtime_checks`] set to `true` cannot read or
|
/// [`ShaderModuleDescriptor::runtime_checks`] set to `true` cannot read or
|
||||||
/// write data via this binding outside the *accessible region* of [`buffer`]:
|
/// write data via this binding outside the *accessible region* of a buffer:
|
||||||
///
|
///
|
||||||
/// - The accessible region starts at [`offset`].
|
/// - The accessible region starts at [`offset`].
|
||||||
///
|
///
|
||||||
@ -2004,14 +2004,14 @@ pub struct PipelineLayoutDescriptor<'a, B: DynBindGroupLayout + ?Sized> {
|
|||||||
/// Some back ends cannot tolerate zero-length regions; for example, see
|
/// Some back ends cannot tolerate zero-length regions; for example, see
|
||||||
/// [VUID-VkDescriptorBufferInfo-offset-00340][340] and
|
/// [VUID-VkDescriptorBufferInfo-offset-00340][340] and
|
||||||
/// [VUID-VkDescriptorBufferInfo-range-00341][341], or the
|
/// [VUID-VkDescriptorBufferInfo-range-00341][341], or the
|
||||||
/// documentation for GLES's [glBindBufferRange][bbr]. For this reason, a valid
|
/// documentation for GLES's [glBindBufferRange][bbr]. This documentation
|
||||||
/// `BufferBinding` must have `offset` strictly less than the size of the
|
/// previously stated that a `BufferBinding` must have `offset` strictly less
|
||||||
/// buffer.
|
/// than the size of the buffer, but this restriction was not honored elsewhere
|
||||||
|
/// in the code, so has been removed. However, it remains the case that
|
||||||
|
/// some backends do not support zero-length bindings, so additional
|
||||||
|
/// logic is needed somewhere to handle this properly. See
|
||||||
|
/// [#3170](https://github.com/gfx-rs/wgpu/issues/3170).
|
||||||
///
|
///
|
||||||
/// WebGPU allows zero-length bindings, and there is not currently a mechanism
|
|
||||||
/// in place
|
|
||||||
///
|
|
||||||
/// [`buffer`]: BufferBinding::buffer
|
|
||||||
/// [`offset`]: BufferBinding::offset
|
/// [`offset`]: BufferBinding::offset
|
||||||
/// [`size`]: BufferBinding::size
|
/// [`size`]: BufferBinding::size
|
||||||
/// [`Storage`]: wgt::BufferBindingType::Storage
|
/// [`Storage`]: wgt::BufferBindingType::Storage
|
||||||
@ -2031,12 +2031,11 @@ pub struct BufferBinding<'a, B: DynBuffer + ?Sized> {
|
|||||||
|
|
||||||
/// The offset at which the bound region starts.
|
/// The offset at which the bound region starts.
|
||||||
///
|
///
|
||||||
/// Because zero-length bindings are not permitted (see above), this must be
|
/// This must be less or equal to the size of the buffer.
|
||||||
/// strictly less than the size of the buffer.
|
|
||||||
pub offset: wgt::BufferAddress,
|
pub offset: wgt::BufferAddress,
|
||||||
|
|
||||||
/// The size of the region bound, in bytes.
|
/// The size of the region bound, in bytes.
|
||||||
pub size: wgt::BufferSize,
|
pub size: wgt::BufferSizeOrZero,
|
||||||
}
|
}
|
||||||
|
|
||||||
// We must implement this manually because `B` is not necessarily `Clone`.
|
// We must implement this manually because `B` is not necessarily `Clone`.
|
||||||
@ -2050,6 +2049,25 @@ impl<B: DynBuffer + ?Sized> Clone for BufferBinding<'_, B> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Temporary convenience trait to let us call `.get()` on `u64`s in code that
|
||||||
|
/// really wants to be using `NonZeroU64`.
|
||||||
|
/// TODO(<https://github.com/gfx-rs/wgpu/issues/3170>): remove this
|
||||||
|
pub trait ShouldBeNonZeroExt {
|
||||||
|
fn get(&self) -> u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ShouldBeNonZeroExt for NonZeroU64 {
|
||||||
|
fn get(&self) -> u64 {
|
||||||
|
NonZeroU64::get(*self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ShouldBeNonZeroExt for u64 {
|
||||||
|
fn get(&self) -> u64 {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a, B: DynBuffer + ?Sized> BufferBinding<'a, B> {
|
impl<'a, B: DynBuffer + ?Sized> BufferBinding<'a, B> {
|
||||||
/// Construct a `BufferBinding` with the given contents.
|
/// Construct a `BufferBinding` with the given contents.
|
||||||
///
|
///
|
||||||
@ -2062,15 +2080,20 @@ impl<'a, B: DynBuffer + ?Sized> BufferBinding<'a, B> {
|
|||||||
///
|
///
|
||||||
/// SAFETY: The caller is responsible for ensuring that a binding of `size`
|
/// SAFETY: The caller is responsible for ensuring that a binding of `size`
|
||||||
/// bytes starting at `offset` is contained within the buffer.
|
/// bytes starting at `offset` is contained within the buffer.
|
||||||
pub unsafe fn new_unchecked(
|
///
|
||||||
|
/// The `S` type parameter is a temporary convenience to allow callers to
|
||||||
|
/// pass either a `u64` or a `NonZeroU64`. When the zero-size binding issue
|
||||||
|
/// is resolved, the argument should just match the type of the member.
|
||||||
|
/// TODO(<https://github.com/gfx-rs/wgpu/issues/3170>): remove the parameter
|
||||||
|
pub unsafe fn new_unchecked<S: Into<wgt::BufferSizeOrZero>>(
|
||||||
buffer: &'a B,
|
buffer: &'a B,
|
||||||
offset: wgt::BufferAddress,
|
offset: wgt::BufferAddress,
|
||||||
size: wgt::BufferSize,
|
size: S,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
buffer,
|
buffer,
|
||||||
offset,
|
offset,
|
||||||
size,
|
size: size.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,7 @@ use alloc::{
|
|||||||
borrow::{Cow, ToOwned as _},
|
borrow::{Cow, ToOwned as _},
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
};
|
};
|
||||||
use core::ops::Range;
|
use core::{num::NonZeroU64, ops::Range};
|
||||||
use metal::{
|
use metal::{
|
||||||
MTLIndexType, MTLLoadAction, MTLPrimitiveType, MTLScissorRect, MTLSize, MTLStoreAction,
|
MTLIndexType, MTLLoadAction, MTLPrimitiveType, MTLScissorRect, MTLSize, MTLStoreAction,
|
||||||
MTLViewport, MTLVisibilityResultMode, NSRange,
|
MTLViewport, MTLVisibilityResultMode, NSRange,
|
||||||
@ -977,9 +977,11 @@ impl crate::CommandEncoder for super::CommandEncoder {
|
|||||||
let encoder = self.state.render.as_ref().unwrap();
|
let encoder = self.state.render.as_ref().unwrap();
|
||||||
encoder.set_vertex_buffer(buffer_index, Some(&binding.buffer.raw), binding.offset);
|
encoder.set_vertex_buffer(buffer_index, Some(&binding.buffer.raw), binding.offset);
|
||||||
|
|
||||||
self.state
|
// https://github.com/gfx-rs/wgpu/issues/3170
|
||||||
.vertex_buffer_size_map
|
let size =
|
||||||
.insert(buffer_index, binding.size);
|
NonZeroU64::new(binding.size).expect("zero-size vertex buffers are not supported");
|
||||||
|
|
||||||
|
self.state.vertex_buffer_size_map.insert(buffer_index, size);
|
||||||
|
|
||||||
if let Some((index, sizes)) = self
|
if let Some((index, sizes)) = self
|
||||||
.state
|
.state
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
use alloc::{borrow::ToOwned as _, sync::Arc, vec::Vec};
|
use alloc::{borrow::ToOwned as _, sync::Arc, vec::Vec};
|
||||||
use core::{ptr::NonNull, sync::atomic};
|
use core::{num::NonZeroU64, ptr::NonNull, sync::atomic};
|
||||||
use std::{thread, time};
|
use std::{thread, time};
|
||||||
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
@ -928,9 +928,12 @@ impl crate::Device for super::Device {
|
|||||||
let end = start + 1;
|
let end = start + 1;
|
||||||
bg.buffers
|
bg.buffers
|
||||||
.extend(desc.buffers[start..end].iter().map(|source| {
|
.extend(desc.buffers[start..end].iter().map(|source| {
|
||||||
|
// https://github.com/gfx-rs/wgpu/issues/3170
|
||||||
|
let source_size = NonZeroU64::new(source.size)
|
||||||
|
.expect("zero-size bindings are not supported");
|
||||||
let binding_size = match ty {
|
let binding_size = match ty {
|
||||||
wgt::BufferBindingType::Storage { .. } => {
|
wgt::BufferBindingType::Storage { .. } => {
|
||||||
Some(source.size)
|
Some(source_size)
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1799,10 +1799,12 @@ impl crate::Device for super::Device {
|
|||||||
(buffer_infos, local_buffer_infos) =
|
(buffer_infos, local_buffer_infos) =
|
||||||
buffer_infos.extend(desc.buffers[start as usize..end as usize].iter().map(
|
buffer_infos.extend(desc.buffers[start as usize..end as usize].iter().map(
|
||||||
|binding| {
|
|binding| {
|
||||||
|
// https://github.com/gfx-rs/wgpu/issues/3170
|
||||||
|
assert!(binding.size != 0, "zero-size bindings are not supported");
|
||||||
vk::DescriptorBufferInfo::default()
|
vk::DescriptorBufferInfo::default()
|
||||||
.buffer(binding.buffer.raw)
|
.buffer(binding.buffer.raw)
|
||||||
.offset(binding.offset)
|
.offset(binding.offset)
|
||||||
.range(binding.size.get())
|
.range(binding.size)
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
write.buffer_info(local_buffer_infos)
|
write.buffer_info(local_buffer_infos)
|
||||||
|
|||||||
@ -61,6 +61,12 @@ pub type BufferAddress = u64;
|
|||||||
/// [`BufferSlice`]: ../wgpu/struct.BufferSlice.html
|
/// [`BufferSlice`]: ../wgpu/struct.BufferSlice.html
|
||||||
pub type BufferSize = core::num::NonZeroU64;
|
pub type BufferSize = core::num::NonZeroU64;
|
||||||
|
|
||||||
|
/// Integral type used for buffer sizes that may be zero.
|
||||||
|
///
|
||||||
|
/// Although the wgpu Rust API disallows zero-size `BufferSlice` and wgpu-hal
|
||||||
|
/// disallows zero-size bindings, WebGPU permits zero-size buffers and bindings.
|
||||||
|
pub type BufferSizeOrZero = u64;
|
||||||
|
|
||||||
/// Integral type used for binding locations in shaders.
|
/// Integral type used for binding locations in shaders.
|
||||||
///
|
///
|
||||||
/// Used in [`VertexAttribute`]s and errors.
|
/// Used in [`VertexAttribute`]s and errors.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user