Skip to content

Commit c0a580d

Browse files
andyleisersonjimblandy
authored andcommitted
Restore unintentional support for zero-size buffers
1 parent ef428fc commit c0a580d

File tree

16 files changed

+129
-101
lines changed

16 files changed

+129
-101
lines changed

wgpu-core/src/binding_model.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ pub enum BindingError {
106106
binding_size: u64,
107107
buffer_size: u64,
108108
},
109-
#[error("Buffer {buffer}: Binding offset {offset} is greater than or equal to buffer size {buffer_size}")]
109+
#[error("Buffer {buffer}: Binding offset {offset} is greater than buffer size {buffer_size}")]
110110
BindingOffsetTooLarge {
111111
buffer: ResourceErrorIdent,
112112
offset: wgt::BufferAddress,

wgpu-core/src/command/bundle.rs

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ use core::{
9393
use arrayvec::ArrayVec;
9494
use thiserror::Error;
9595

96+
use wgpu_hal::ShouldBeNonZeroExt;
9697
use wgt::error::{ErrorType, WebGpuError};
9798

9899
use crate::{
@@ -504,7 +505,7 @@ impl RenderBundleEncoder {
504505
buffer_id,
505506
index_format,
506507
offset,
507-
size,
508+
size: size.map(NonZeroU64::get),
508509
});
509510
}
510511
}
@@ -609,7 +610,7 @@ fn set_index_buffer(
609610
buffer_id: id::Id<id::markers::Buffer>,
610611
index_format: wgt::IndexFormat,
611612
offset: u64,
612-
size: Option<NonZeroU64>,
613+
size: Option<wgt::BufferSizeOrZero>,
613614
) -> Result<(), RenderBundleErrorInner> {
614615
let buffer = buffer_guard.get(buffer_id).get()?;
615616

@@ -641,7 +642,7 @@ fn set_vertex_buffer(
641642
slot: u32,
642643
buffer_id: id::Id<id::markers::Buffer>,
643644
offset: u64,
644-
size: Option<NonZeroU64>,
645+
size: Option<wgt::BufferSizeOrZero>,
645646
) -> Result<(), RenderBundleErrorInner> {
646647
let max_vertex_buffers = state.device.limits.max_vertex_buffers;
647648
if slot >= max_vertex_buffers {
@@ -1166,19 +1167,16 @@ impl IndexState {
11661167
.range
11671168
.end
11681169
.checked_sub(self.range.start)
1169-
.and_then(wgt::BufferSize::new);
1170-
assert!(
1171-
self.range.end <= self.buffer.size && binding_size.is_some(),
1172-
"index buffer range must have non-zero size and be contained in buffer",
1173-
);
1170+
.filter(|_| self.range.end <= self.buffer.size)
1171+
.expect("index range must be contained in buffer");
11741172

11751173
if self.is_dirty {
11761174
self.is_dirty = false;
11771175
Some(ArcRenderCommand::SetIndexBuffer {
11781176
buffer: self.buffer.clone(),
11791177
index_format: self.format,
11801178
offset: self.range.start,
1181-
size: binding_size,
1179+
size: Some(binding_size),
11821180
})
11831181
} else {
11841182
None
@@ -1221,24 +1219,20 @@ impl VertexState {
12211219
///
12221220
/// `slot` is the index of the vertex buffer slot that `self` tracks.
12231221
fn flush(&mut self, slot: u32) -> Option<ArcRenderCommand> {
1224-
// This was all checked before, but let's check again just in case.
12251222
let binding_size = self
12261223
.range
12271224
.end
12281225
.checked_sub(self.range.start)
1229-
.and_then(wgt::BufferSize::new);
1230-
assert!(
1231-
self.range.end <= self.buffer.size && binding_size.is_some(),
1232-
"vertex buffer range must have non-zero size and be contained in buffer",
1233-
);
1226+
.filter(|_| self.range.end <= self.buffer.size)
1227+
.expect("vertex range must be contained in buffer");
12341228

12351229
if self.is_dirty {
12361230
self.is_dirty = false;
12371231
Some(ArcRenderCommand::SetVertexBuffer {
12381232
slot,
12391233
buffer: self.buffer.clone(),
12401234
offset: self.range.start,
1241-
size: binding_size,
1235+
size: Some(binding_size),
12421236
})
12431237
} else {
12441238
None
@@ -1602,7 +1596,7 @@ where
16021596
pub mod bundle_ffi {
16031597
use super::{RenderBundleEncoder, RenderCommand};
16041598
use crate::{id, RawString};
1605-
use core::{convert::TryInto, slice};
1599+
use core::{convert::TryInto, num::NonZeroU64, slice};
16061600
use wgt::{BufferAddress, BufferSize, DynamicOffset, IndexFormat};
16071601

16081602
/// # Safety
@@ -1661,7 +1655,7 @@ pub mod bundle_ffi {
16611655
slot,
16621656
buffer_id,
16631657
offset,
1664-
size,
1658+
size: size.map(NonZeroU64::get),
16651659
});
16661660
}
16671661

wgpu-core/src/command/render.rs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
use alloc::{borrow::Cow, sync::Arc, vec::Vec};
2-
use core::{fmt, num::NonZeroU32, str};
2+
use core::{
3+
fmt,
4+
num::{NonZeroU32, NonZeroU64},
5+
str,
6+
};
7+
use hal::ShouldBeNonZeroExt;
38

49
use arrayvec::ArrayVec;
510
use thiserror::Error;
611
use wgt::{
712
error::{ErrorType, WebGpuError},
8-
BufferAddress, BufferSize, BufferUsages, Color, DynamicOffset, IndexFormat, ShaderStages,
9-
TextureSelector, TextureUsages, TextureViewDimension, VertexStepMode,
13+
BufferAddress, BufferSize, BufferSizeOrZero, BufferUsages, Color, DynamicOffset, IndexFormat,
14+
ShaderStages, TextureSelector, TextureUsages, TextureViewDimension, VertexStepMode,
1015
};
1116

1217
use crate::command::{
@@ -2333,7 +2338,7 @@ fn set_index_buffer(
23332338
buffer: Arc<crate::resource::Buffer>,
23342339
index_format: IndexFormat,
23352340
offset: u64,
2336-
size: Option<BufferSize>,
2341+
size: Option<BufferSizeOrZero>,
23372342
) -> Result<(), RenderPassErrorInner> {
23382343
api_log!("RenderPass::set_index_buffer {}", buffer.error_ident());
23392344

@@ -2373,7 +2378,7 @@ fn set_vertex_buffer(
23732378
slot: u32,
23742379
buffer: Arc<crate::resource::Buffer>,
23752380
offset: u64,
2376-
size: Option<BufferSize>,
2381+
size: Option<BufferSizeOrZero>,
23772382
) -> Result<(), RenderPassErrorInner> {
23782383
api_log!(
23792384
"RenderPass::set_vertex_buffer {slot} {}",
@@ -3084,7 +3089,7 @@ impl Global {
30843089
buffer: pass_try!(base, scope, self.resolve_render_pass_buffer_id(buffer_id)),
30853090
index_format,
30863091
offset,
3087-
size,
3092+
size: size.map(NonZeroU64::get),
30883093
});
30893094

30903095
Ok(())
@@ -3105,7 +3110,7 @@ impl Global {
31053110
slot,
31063111
buffer: pass_try!(base, scope, self.resolve_render_pass_buffer_id(buffer_id)),
31073112
offset,
3108-
size,
3113+
size: size.map(NonZeroU64::get),
31093114
});
31103115

31113116
Ok(())

wgpu-core/src/command/render_command.rs

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use alloc::sync::Arc;
22

3-
use wgt::{BufferAddress, BufferSize, Color};
3+
use wgt::{BufferAddress, BufferSizeOrZero, Color};
44

55
use super::{Rect, RenderBundle};
66
use crate::{
@@ -24,13 +24,13 @@ pub enum RenderCommand {
2424
buffer_id: id::BufferId,
2525
index_format: wgt::IndexFormat,
2626
offset: BufferAddress,
27-
size: Option<BufferSize>,
27+
size: Option<BufferSizeOrZero>,
2828
},
2929
SetVertexBuffer {
3030
slot: u32,
3131
buffer_id: id::BufferId,
3232
offset: BufferAddress,
33-
size: Option<BufferSize>,
33+
size: Option<BufferSizeOrZero>,
3434
},
3535
SetBlendConstant(Color),
3636
SetStencilReference(u32),
@@ -418,21 +418,18 @@ pub enum ArcRenderCommand {
418418
offset: BufferAddress,
419419

420420
// For a render pass, this reflects the argument passed by the
421-
// application, which may be `None`. For a render bundle, this reflects
422-
// the validated size of the binding, and will be populated even in the
423-
// case that the application omitted the size.
424-
size: Option<BufferSize>,
421+
// application, which may be `None`. For a finished render bundle, this
422+
// reflects the validated size of the binding, and will be populated
423+
// even in the case that the application omitted the size.
424+
size: Option<BufferSizeOrZero>,
425425
},
426426
SetVertexBuffer {
427427
slot: u32,
428428
buffer: Arc<Buffer>,
429429
offset: BufferAddress,
430430

431-
// For a render pass, this reflects the argument passed by the
432-
// application, which may be `None`. For a render bundle, this reflects
433-
// the validated size of the binding, and will be populated even in the
434-
// case that the application omitted the size.
435-
size: Option<BufferSize>,
431+
// See comment in `SetIndexBuffer`.
432+
size: Option<BufferSizeOrZero>,
436433
},
437434
SetBlendConstant(Color),
438435
SetStencilReference(u32),

wgpu-core/src/device/resource.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ use alloc::{
88
use core::{
99
fmt,
1010
mem::{self, ManuallyDrop},
11-
num::NonZeroU32,
11+
num::{NonZeroU32, NonZeroU64},
1212
sync::atomic::{AtomicBool, Ordering},
1313
};
14+
use hal::ShouldBeNonZeroExt;
1415

1516
use arrayvec::ArrayVec;
1617
use bitflags::Flags;
@@ -2197,7 +2198,7 @@ impl Device {
21972198

21982199
buffer.check_usage(pub_usage)?;
21992200

2200-
let bb = buffer.binding(bb.offset, bb.size, snatch_guard)?;
2201+
let bb = buffer.binding(bb.offset, bb.size.map(NonZeroU64::get), snatch_guard)?;
22012202
let bind_size = bb.size.get();
22022203

22032204
if bind_size > range_limit as u64 {

wgpu-core/src/resource.rs

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -490,35 +490,32 @@ impl Buffer {
490490
/// If `size` is `None`, then the remainder of the buffer starting from
491491
/// `offset` is used.
492492
///
493-
/// If the binding would overflow the buffer or is empty (see
494-
/// [`hal::BufferBinding`]), then an error is returned.
493+
/// If the binding would overflow the buffer, then an error is returned.
494+
///
495+
/// Zero-size bindings are permitted here for historical reasons. Although
496+
/// zero-size bindings are permitted by WebGPU, they are not permitted by
497+
/// some backends. See [`Buffer::binding`] and
498+
/// [#3170](https://github.com/gfx-rs/wgpu/issues/3170).
495499
pub fn resolve_binding_size(
496500
&self,
497501
offset: wgt::BufferAddress,
498-
binding_size: Option<wgt::BufferSize>,
499-
) -> Result<wgt::BufferSize, BindingError> {
502+
binding_size: Option<wgt::BufferSizeOrZero>,
503+
) -> Result<wgt::BufferSizeOrZero, BindingError> {
500504
let buffer_size = self.size;
501505

502506
match binding_size {
503-
Some(binding_size) => {
504-
match offset.checked_add(binding_size.get()) {
505-
// `binding_size` is not zero which means `end == buffer_size` is ok.
506-
Some(end) if end <= buffer_size => Ok(binding_size),
507-
_ => Err(BindingError::BindingRangeTooLarge {
508-
buffer: self.error_ident(),
509-
offset,
510-
binding_size: binding_size.get(),
511-
buffer_size,
512-
}),
513-
}
514-
}
507+
Some(binding_size) => match offset.checked_add(binding_size) {
508+
Some(end) if end <= buffer_size => Ok(binding_size),
509+
_ => Err(BindingError::BindingRangeTooLarge {
510+
buffer: self.error_ident(),
511+
offset,
512+
binding_size,
513+
buffer_size,
514+
}),
515+
},
515516
None => {
516-
// We require that `buffer_size - offset` converts to
517-
// `BufferSize` (`NonZeroU64`) because bindings must not be
518-
// empty.
519517
buffer_size
520518
.checked_sub(offset)
521-
.and_then(wgt::BufferSize::new)
522519
.ok_or_else(|| BindingError::BindingOffsetTooLarge {
523520
buffer: self.error_ident(),
524521
offset,
@@ -534,12 +531,20 @@ impl Buffer {
534531
/// If `size` is `None`, then the remainder of the buffer starting from
535532
/// `offset` is used.
536533
///
537-
/// If the binding would overflow the buffer or is empty (see
538-
/// [`hal::BufferBinding`]), then an error is returned.
534+
/// If the binding would overflow the buffer, then an error is returned.
535+
///
536+
/// Zero-size bindings are permitted here for historical reasons. Although
537+
/// zero-size bindings are permitted by WebGPU, they are not permitted by
538+
/// some backends. Previous documentation for `hal::BufferBinding`
539+
/// disallowed zero-size bindings, but this restriction was not honored
540+
/// elsewhere in the code. Zero-size bindings need to be quashed or remapped
541+
/// to a non-zero size, either universally in wgpu-core, or in specific
542+
/// backends that do not support them. See
543+
/// [#3170](https://github.com/gfx-rs/wgpu/issues/3170).
539544
pub fn binding<'a>(
540545
&'a self,
541546
offset: wgt::BufferAddress,
542-
binding_size: Option<wgt::BufferSize>,
547+
binding_size: Option<wgt::BufferSizeOrZero>,
543548
snatch_guard: &'a SnatchGuard,
544549
) -> Result<hal::BufferBinding<'a, dyn hal::DynBuffer>, BindingError> {
545550
let buf_raw = self.try_raw(snatch_guard)?;

wgpu-hal/examples/halmark/main.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -447,11 +447,7 @@ impl<A: hal::Api> Example<A> {
447447
let global_group = {
448448
let global_buffer_binding = unsafe {
449449
// SAFETY: This is the same size that was specified for buffer creation.
450-
hal::BufferBinding::new_unchecked(
451-
&global_buffer,
452-
0,
453-
global_buffer_desc.size.try_into().unwrap(),
454-
)
450+
hal::BufferBinding::new_unchecked(&global_buffer, 0, global_buffer_desc.size)
455451
};
456452
let texture_binding = hal::TextureBinding {
457453
view: &texture_view,

wgpu-hal/src/dx12/command.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1136,7 +1136,7 @@ impl crate::CommandEncoder for super::CommandEncoder {
11361136
) {
11371137
let ibv = Direct3D12::D3D12_INDEX_BUFFER_VIEW {
11381138
BufferLocation: binding.resolve_address(),
1139-
SizeInBytes: binding.resolve_size() as u32,
1139+
SizeInBytes: binding.size.try_into().unwrap(),
11401140
Format: auxil::dxgi::conv::map_index_format(format),
11411141
};
11421142

@@ -1149,7 +1149,7 @@ impl crate::CommandEncoder for super::CommandEncoder {
11491149
) {
11501150
let vb = &mut self.pass.vertex_buffers[index as usize];
11511151
vb.BufferLocation = binding.resolve_address();
1152-
vb.SizeInBytes = binding.resolve_size() as u32;
1152+
vb.SizeInBytes = binding.size.try_into().unwrap();
11531153
self.pass.dirty_vertex_buffers |= 1 << index;
11541154
}
11551155

wgpu-hal/src/dx12/device.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1442,7 +1442,7 @@ impl crate::Device for super::Device {
14421442
let end = start + entry.count as usize;
14431443
for data in &desc.buffers[start..end] {
14441444
let gpu_address = data.resolve_address();
1445-
let mut size = data.resolve_size() as u32;
1445+
let mut size = data.size.try_into().unwrap();
14461446

14471447
if has_dynamic_offset {
14481448
match ty {

wgpu-hal/src/dx12/mod.rs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -865,13 +865,6 @@ unsafe impl Sync for Buffer {}
865865
impl crate::DynBuffer for Buffer {}
866866

867867
impl crate::BufferBinding<'_, Buffer> {
868-
fn resolve_size(&self) -> wgt::BufferAddress {
869-
match self.size {
870-
Some(size) => size.get(),
871-
None => self.buffer.size - self.offset,
872-
}
873-
}
874-
875868
// TODO: Return GPU handle directly?
876869
fn resolve_address(&self) -> wgt::BufferAddress {
877870
(unsafe { self.buffer.resource.GetGPUVirtualAddress() }) + self.offset

0 commit comments

Comments
 (0)