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] destinationBytesPerImage should probably be 0 when copying to/from a 2d texture with #3062

Closed
nical opened this issue Oct 5, 2022 · 0 comments · Fixed by #3063
Closed

Comments

@nical
Copy link
Contributor

nical commented Oct 5, 2022

Description

When using queue.write_texture to update a subset of a simple 2d texture (no arrays, no mipmaps, etc), metal validation fails with an error of the form:

-[MTLDebugBlitCommandEncoder validateCopyFromBuffer:sourceOffset:sourceBytesPerRow:sourceBytesPerImage:sourceSize:toTexture:destinationSlice:destinationLevel:destinationOrigin:options:]:804: failed assertion `Copy From Buffer Validation
totalBytesUsed(1048576) must be <= [sourceBuffer length](2048).

where totalBytesUsed is equal to the total size of the texture (+ offset if there is one) and sourceBuffer length is the amount of data being passed for that subset of the image that we are trying to update.

The program runs on its own, but validation fails for example when running in xcode which prevents using the associated gpu tooling.

Relevant metal doc: https://developer.apple.com/documentation/metal/mtlblitcommandencoder/1400752-copy

The problem seems to come from destinationBytesPerImage which (somewhat confusingly) expresses the stride between layers of the image (for array textures and 3d textures) rather than the image size (same quantity but not same purpose).

The closest I could find online of someone running into a similar issue is: https://stackoverflow.com/questions/43069257/how-to-use-mtlblitcommandencoder-for-copying-interlaced-video-fields-into-a-mtlb

The answer suggests passing 0 as the destinationBytesPerImage, I think that it sounds reasonable.

Note that letting the user pass None in rows_per_image does not work around the problem because of

let block_rows_per_image = match data_layout.rows_per_image {

Repro steps

        let tex = device.create_texture(&wgpu::TextureDescriptor {
            label: None,
            dimension: wgpu::TextureDimension::D2,
            size: wgpu::Extent3d {
                width: 1024,
                height: 1024,
                depth_or_array_layers: 1,
            },
            format: wgpu::TextureFormat::R8Uint,
            usage: wgpu::TextureUsages::COPY_DST | wgpu::TextureUsages::TEXTURE_BINDING,
            mip_level_count: 1,
            sample_count: 1,
        });
        let data = vec![1u8; 1024 * 2];
        // Write the first two rows
        queue.write_texture(
            wgpu::ImageCopyTexture {
                texture: &tex,
                mip_level: 0,
                origin: wgpu::Origin3d::ZERO,
                aspect: wgpu::TextureAspect::All,
            },
            bytemuck::cast_slice(&data),
            wgpu::ImageDataLayout {
                offset: 0,
                bytes_per_row: std::num::NonZeroU32::new(1024),
                rows_per_image: std::num::NonZeroU32::new(1024),
            },
            wgpu::Extent3d {
                width: 1024,
                height: 2,
                depth_or_array_layers: 1,
            }
        );

Expected vs observed behavior

Expect the code to run fine when validation is enabled (for example when running in xcode).

Platform

MacOS, wgpu 0.13.1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant