{"record":{"id":"fde283748147fe16","repo":"emilk/egui","slug":"failed-to-create-image","errorCode":null,"errorMessage":"Failed to create image","messagePattern":"Failed to create image","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/egui_kittest/src/texture_to_image.rs","lineNumber":70,"sourceCode":"    device\n        .poll(wgpu::PollType::Wait {\n            submission_index: Some(submission_index),\n            timeout: Some(WAIT_TIMEOUT),\n        })\n        .expect(\"Failed to poll device\");\n\n    receiver.recv().unwrap().unwrap();\n    let buffer_slice = output_buffer.slice(..);\n    let data = buffer_slice\n        .get_mapped_range()\n        .expect(\"Failed to get mapped range\");\n    let data = data\n        .chunks_exact(buffer_dimensions.padded_bytes_per_row)\n        .flat_map(|row| row.iter().take(buffer_dimensions.unpadded_bytes_per_row))\n        .copied()\n        .collect::<Vec<_>>();\n\n    RgbaImage::from_raw(texture.width(), texture.height(), data).expect(\"Failed to create image\")\n}\n\nstruct BufferDimensions {\n    height: usize,\n    unpadded_bytes_per_row: usize,\n    padded_bytes_per_row: usize,\n}\n\nimpl BufferDimensions {\n    fn new(width: usize, height: usize) -> Self {\n        let bytes_per_pixel = size_of::<u32>();\n        let unpadded_bytes_per_row = width * bytes_per_pixel;\n        let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT as usize;\n        let padded_bytes_per_row_padding = (align - unpadded_bytes_per_row % align) % align;\n        let padded_bytes_per_row = unpadded_bytes_per_row + padded_bytes_per_row_padding;\n        Self {\n            height,\n            unpadded_bytes_per_row,","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/emilk/egui/blob/441971a776322a482e371775219380eca812cfa9/crates/egui_kittest/src/texture_to_image.rs#L52-L88","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Assert data.len() == texture.width() * texture.height() * 4 before from_raw and print both numbers to find the accounting bug","Verify padded_bytes_per_row is computed as the wgpu-mandated stride ((bytes_per_row + 255) / 256 * 256), matching the buffer mapping","Recreate the texture/view with TextureFormat::Rgba8Unorm (or convert the copy) before snapshotting","Replace the expect with a match that returns a descriptive Result so the size mismatch is reported, not panicked"],"exampleFix":"// before\nRgbaImage::from_raw(texture.width(), texture.height(), data).expect(\"Failed to create image\")\n// after\nlet expected = texture.width() as usize * texture.height() as usize * 4;\nassert_eq!(data.len(), expected, \"texture byte count mismatch: {} vs {}\", data.len(), expected);\nRgbaImage::from_raw(texture.width(), texture.height(), data)\n    .expect(\"Failed to create image: byte count does not match width*height*4\")","handlingStrategy":"validation","validationCode":"let expected = (texture.width() as usize) * (texture.height() as usize) * 4;\nassert_eq!(data.len(), expected, \"texture bytes {} != {} (w={}, h={})\", data.len(), expected, texture.width(), texture.height());","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always snapshot textures in Rgba8Unorm (or convert first); other formats change bytes-per-pixel","Compute padded_bytes_per_row with wgpu's 256-byte alignment rule: (bpr + 255) / 256 * 256","Recompute BufferDimensions whenever width/height/format changes","Assert byte-count invariants in tests instead of relying on the final expect"],"tags":["gpu","wgpu","image","panic","testing"],"backgroundTag":"shape-mismatch","analyzedSha":"441971a776322a482e371775219380eca812cfa9","analyzedAt":"2026-09-12T04:29:21.500Z","contentChangedAt":"2026-09-12T04:29:21.500Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}