Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[metal] set the image stride to zero when updating a single layer of a 3D texture #4154

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
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
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,9 @@ jobs:

for backend in ${{ matrix.backends }}; do
echo "======= NATIVE TESTS $backend ======";
if ["$backend" == "metal"]; then
echo "METAL_DEVICE_WRAPPER_TYPE=1" >> "$GITHUB_ENV"
fi
WGPU_BACKEND=$backend cargo llvm-cov --no-cfg-coverage nextest --no-fail-fast --no-report --features vulkan-portability
done

Expand Down
52 changes: 43 additions & 9 deletions tests/tests/write_texture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,16 @@ fn write_texture_subset_2d() {

ctx.queue.submit(None);

let read_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {
let read_buffer_0 = ctx.device.create_buffer(&wgpu::BufferDescriptor {
Copy link
Member

Choose a reason for hiding this comment

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

would imho be nice to parameterize this test to have it both do a full update and two piecewise.
The second one can then point to this PR, pointing out that it protects against regressing this

Copy link
Member Author

Choose a reason for hiding this comment

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

I can probably trigger the validation error with a simpler test case.
Will try to do that instead as opposed to modifying this one.

label: None,
size: (size * size) as u64,
size: (size * 2) as u64,
usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});

let read_buffer_1 = ctx.device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: (size * (size - 2)) as u64,
usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
Expand All @@ -70,7 +77,7 @@ fn write_texture_subset_2d() {
aspect: wgpu::TextureAspect::All,
},
wgpu::ImageCopyBuffer {
buffer: &read_buffer,
buffer: &read_buffer_0,
layout: wgpu::ImageDataLayout {
offset: 0,
bytes_per_row: Some(size),
Expand All @@ -79,22 +86,49 @@ fn write_texture_subset_2d() {
},
wgpu::Extent3d {
width: size,
height: size,
height: 2,
depth_or_array_layers: 1,
},
);

encoder.copy_texture_to_buffer(
wgpu::ImageCopyTexture {
texture: &tex,
mip_level: 0,
origin: wgpu::Origin3d { x: 0, y: 2, z: 0 },
aspect: wgpu::TextureAspect::All,
},
wgpu::ImageCopyBuffer {
buffer: &read_buffer_1,
layout: wgpu::ImageDataLayout {
offset: 0,
bytes_per_row: Some(size),
rows_per_image: Some(size),
},
},
wgpu::Extent3d {
width: size,
height: size - 2,
depth_or_array_layers: 1,
},
);

ctx.queue.submit(Some(encoder.finish()));

let slice = read_buffer.slice(..);
slice.map_async(wgpu::MapMode::Read, |_| ());
let slice_0 = read_buffer_0.slice(..);
slice_0.map_async(wgpu::MapMode::Read, |_| ());
let slice_1 = read_buffer_1.slice(..);
slice_1.map_async(wgpu::MapMode::Read, |_| ());

ctx.device.poll(wgpu::Maintain::Wait);
let data: Vec<u8> = slice.get_mapped_range().to_vec();

for byte in &data[..(size as usize * 2)] {
let data_0 = slice_0.get_mapped_range();
let data_1 = slice_1.get_mapped_range();

for byte in data_0.as_ref() {
assert_eq!(*byte, 1);
}
for byte in &data[(size as usize * 2)..] {
for byte in data_1.as_ref() {
assert_eq!(*byte, 0);
}
});
Expand Down
16 changes: 11 additions & 5 deletions wgpu-hal/src/metal/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -360,10 +360,16 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
.max_copy_size(&src.copy_size)
.min(&copy.size);
let bytes_per_row = copy.buffer_layout.bytes_per_row.unwrap_or(0) as u64;
let bytes_per_image = copy
.buffer_layout
.rows_per_image
.map_or(0, |v| v as u64 * bytes_per_row);
let image_byte_stride = if extent.depth > 1 {
copy.buffer_layout
.rows_per_image
.map_or(0, |v| v as u64 * bytes_per_row)
} else {
// Don't pass a stride when updating a single layer, otherwise metal validation
// fails when updating a subset of the image due to the stride being larger than
// the amount of data to copy.
0
};
encoder.copy_from_texture_to_buffer(
&src.raw,
copy.texture_base.array_layer as u64,
Expand All @@ -373,7 +379,7 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
&dst.raw,
copy.buffer_layout.offset,
bytes_per_row,
bytes_per_image,
image_byte_stride,
conv::get_blit_option(src.format, copy.texture_base.aspect),
);
}
Expand Down
Loading