Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ This allows using precompiled shaders without manually checking which backend's
By @kpreid in [#8011](https://github.com/gfx-rs/wgpu/pull/8011).
- Make a compacted hal acceleration structure inherit a label from the base BLAS. By @Vecvec in [#8103](https://github.com/gfx-rs/wgpu/pull/8103).
- The limits requested for a device must now satisfy `min_subgroup_size <= max_subgroup_size`. By @andyleiserson in [#8085](https://github.com/gfx-rs/wgpu/pull/8085).
- Improve errors when buffer mapping is done incorrectly. Allow aliasing immutable [`BufferViews`]. By @cwfitzgerald in [#8150](https://github.com/gfx-rs/wgpu/pull/8150).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's add a highlight for this & removed lifetimes on buffer mappings. If you're used to working around these this thing is a lifechanger!


#### Naga

Expand Down
191 changes: 191 additions & 0 deletions tests/tests/wgpu-validation/api/buffer_mapping.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
fn mapping_is_zeroed(array: &[u8]) {
for (i, &byte) in array.iter().enumerate() {
assert_eq!(byte, 0, "Byte at index {i} is not zero");
}
}

// Ensure that a simple immutable mapping works and it is zeroed.
#[test]
fn full_immutable_binding() {
let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());

let buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: 1024,
usage: wgpu::BufferUsages::MAP_READ,
mapped_at_creation: false,
});

buffer.map_async(wgpu::MapMode::Read, .., |_| {});
device.poll(wgpu::PollType::Wait).unwrap();

let mapping = buffer.slice(..).get_mapped_range();

mapping_is_zeroed(&mapping);

drop(mapping);

buffer.unmap();
}

// Ensure that a simple mutable binding works and it is zeroed.
#[test]
fn full_mut_binding() {
let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());

let buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: 1024,
usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC,
mapped_at_creation: true,
});

let mapping = buffer.slice(..).get_mapped_range_mut();

mapping_is_zeroed(&mapping);

drop(mapping);

buffer.unmap();
}

// Ensure that you can make two non-overlapping immutable ranges, which are both zeroed
#[test]
fn split_immutable_binding() {
let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());

let buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: 1024,
usage: wgpu::BufferUsages::MAP_READ,
mapped_at_creation: false,
});

buffer.map_async(wgpu::MapMode::Read, .., |_| {});
device.poll(wgpu::PollType::Wait).unwrap();

let mapping0 = buffer.slice(0..512).get_mapped_range();
let mapping1 = buffer.slice(512..1024).get_mapped_range();

mapping_is_zeroed(&mapping0);
mapping_is_zeroed(&mapping1);

drop(mapping0);
drop(mapping1);

buffer.unmap();
}

/// Ensure that you can make two non-overlapping mapped ranges, which are both zeroed
#[test]
fn split_mut_binding() {
let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());

let buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: 1024,
usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC,
mapped_at_creation: true,
});

let mapping0 = buffer.slice(0..512).get_mapped_range_mut();
let mapping1 = buffer.slice(512..1024).get_mapped_range_mut();

mapping_is_zeroed(&mapping0);
mapping_is_zeroed(&mapping1);

drop(mapping0);
drop(mapping1);

buffer.unmap();
}

/// Ensure that you can make two overlapping immutablely mapped ranges.
#[test]
fn overlapping_ref_binding() {
let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());

let buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: 1024,
usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC,
mapped_at_creation: true,
});

let _mapping0 = buffer.slice(0..512).get_mapped_range();
let _mapping1 = buffer.slice(256..768).get_mapped_range();
}

/// Ensure that two overlapping mutably mapped ranges panics.
#[test]
#[should_panic(expected = "break Rust memory aliasing rules")]
fn overlapping_mut_binding() {
let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());

let buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: 1024,
usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC,
mapped_at_creation: true,
});

let _mapping0 = buffer.slice(0..512).get_mapped_range_mut();
let _mapping1 = buffer.slice(256..768).get_mapped_range_mut();
}

/// Ensure that when you try to get a mapped range from an unmapped buffer, it panics with
/// an error mentioning a completely unmapped buffer.
#[test]
#[should_panic(expected = "an unmapped buffer")]
fn not_mapped() {
let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());

let buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: 1024,
usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC,
mapped_at_creation: false,
});

let _mapping = buffer.slice(..).get_mapped_range_mut();
}

/// Ensure that when you partially map a buffer, then try to read outside of that range, it panics
/// mentioning the mapped indices.
#[test]
#[should_panic(
expected = "Attempted to get range 512..1024 (Mutable), but the mapped range is 0..512"
)]
fn partially_mapped() {
let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());

let buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: 1024,
usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC,
mapped_at_creation: false,
});

buffer.map_async(wgpu::MapMode::Write, 0..512, |_| {});
device.poll(wgpu::PollType::Wait).unwrap();

let _mapping0 = buffer.slice(0..512).get_mapped_range_mut();
let _mapping1 = buffer.slice(512..1024).get_mapped_range_mut();
}

/// Ensure that you cannot unmap a buffer while there are still accessible mapped views.
#[test]
#[should_panic(expected = "You cannot unmap a buffer that still has accessible mapped views")]
fn unmap_while_visible() {
let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());

let buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: 1024,
usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC,
mapped_at_creation: true,
});

let _mapping0 = buffer.slice(..).get_mapped_range_mut();
buffer.unmap();
}
1 change: 1 addition & 0 deletions tests/tests/wgpu-validation/api/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod binding_arrays;
mod buffer;
mod buffer_mapping;
mod buffer_slice;
mod device;
mod external_texture;
Expand Down
Loading
Loading