emilk/egui · error

Failed to create image

Error message

Failed to create image

What it means

texture_to_image() strips the per-row padding from a GPU buffer copy and hands the tightly packed RGBA bytes to image::RgbaImage::from_raw, expecting the created image. from_raw returns None whenever data.len() != width * height * 4, and the code panics with "Failed to create image". This is an internal size invariant: the BufferDimensions bookkeeping must exactly match the texture's byte layout.

Source

Thrown at crates/egui_kittest/src/texture_to_image.rs:70

    device
        .poll(wgpu::PollType::Wait {
            submission_index: Some(submission_index),
            timeout: Some(WAIT_TIMEOUT),
        })
        .expect("Failed to poll device");

    receiver.recv().unwrap().unwrap();
    let buffer_slice = output_buffer.slice(..);
    let data = buffer_slice
        .get_mapped_range()
        .expect("Failed to get mapped range");
    let data = data
        .chunks_exact(buffer_dimensions.padded_bytes_per_row)
        .flat_map(|row| row.iter().take(buffer_dimensions.unpadded_bytes_per_row))
        .copied()
        .collect::<Vec<_>>();

    RgbaImage::from_raw(texture.width(), texture.height(), data).expect("Failed to create image")
}

struct BufferDimensions {
    height: usize,
    unpadded_bytes_per_row: usize,
    padded_bytes_per_row: usize,
}

impl BufferDimensions {
    fn new(width: usize, height: usize) -> Self {
        let bytes_per_pixel = size_of::<u32>();
        let unpadded_bytes_per_row = width * bytes_per_pixel;
        let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT as usize;
        let padded_bytes_per_row_padding = (align - unpadded_bytes_per_row % align) % align;
        let padded_bytes_per_row = unpadded_bytes_per_row + padded_bytes_per_row_padding;
        Self {
            height,
            unpadded_bytes_per_row,

View on GitHub (pinned to 441971a776)

Solutions

  1. Assert data.len() == texture.width() * texture.height() * 4 before from_raw and print both numbers to find the accounting bug
  2. Verify padded_bytes_per_row is computed as the wgpu-mandated stride ((bytes_per_row + 255) / 256 * 256), matching the buffer mapping
  3. Recreate the texture/view with TextureFormat::Rgba8Unorm (or convert the copy) before snapshotting
  4. Replace the expect with a match that returns a descriptive Result so the size mismatch is reported, not panicked

Example fix

// before
RgbaImage::from_raw(texture.width(), texture.height(), data).expect("Failed to create image")
// after
let expected = texture.width() as usize * texture.height() as usize * 4;
assert_eq!(data.len(), expected, "texture byte count mismatch: {} vs {}", data.len(), expected);
RgbaImage::from_raw(texture.width(), texture.height(), data)
    .expect("Failed to create image: byte count does not match width*height*4")
Defensive patterns

Strategy: validation

Validate before calling

let expected = (texture.width() as usize) * (texture.height() as usize) * 4;
assert_eq!(data.len(), expected, "texture bytes {} != {} (w={}, h={})", data.len(), expected, texture.width(), texture.height());

Prevention

When it happens

Trigger: A texture whose bytes-per-pixel is not 4 (e.g. BGRA/RGB formats) fed through the RGBA4 assumption; a mismatch between buffer_dimensions.padded_bytes_per_row used in chunks_exact and the actual buffer row stride returned by the wgpu copy; width/height changed after the buffer size was computed.

Common situations: kittest snapshot tests capturing non-Rgba8Unorm textures; GPU adapters that report a different buffer row alignment (256-byte requirement) than the calculated padded_bytes_per_row; downscaling or resizing textures in a harness without recomputing BufferDimensions.

Related errors


AI-assisted analysis of emilk/egui@441971a776 (2026-09-12). Data as JSON: /api/errors/fde283748147fe16. Report an issue: GitHub.