-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
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
Fix crash when loading textures from gltf. #7005
Closed
Closed
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
d307007
Refine image texture conversion to fix crash when loading specified t…
AllenDang 8a01d35
Add comment to state DynamicImage is now non exhaustive
AllenDang 6677989
Merge pull request #1 from bevyengine/main
AllenDang 95cdfc7
Merge pull request #2 from bevyengine/main
AllenDang File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
148 changes: 21 additions & 127 deletions
148
crates/bevy_render/src/texture/image_texture_conversion.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,156 +1,50 @@ | ||
use crate::texture::{Image, TextureFormatPixelInfo}; | ||
use crate::texture::Image; | ||
use anyhow::anyhow; | ||
use image::{DynamicImage, ImageBuffer}; | ||
use image::{buffer::ConvertBuffer, DynamicImage, ImageBuffer, Luma, Rgba}; | ||
use wgpu::{Extent3d, TextureDimension, TextureFormat}; | ||
|
||
impl Image { | ||
/// Converts a [`DynamicImage`] to an [`Image`]. | ||
pub fn from_dynamic(dyn_img: DynamicImage, is_srgb: bool) -> Image { | ||
use bevy_core::cast_slice; | ||
let width; | ||
let height; | ||
let width = dyn_img.width(); | ||
let height = dyn_img.height(); | ||
|
||
let data: Vec<u8>; | ||
let format: TextureFormat; | ||
|
||
match dyn_img { | ||
DynamicImage::ImageLuma8(i) => { | ||
let i = DynamicImage::ImageLuma8(i).into_rgba8(); | ||
width = i.width(); | ||
height = i.height(); | ||
format = if is_srgb { | ||
TextureFormat::Rgba8UnormSrgb | ||
} else { | ||
TextureFormat::Rgba8Unorm | ||
}; | ||
|
||
format = TextureFormat::R8Unorm; | ||
data = i.into_raw(); | ||
} | ||
DynamicImage::ImageLumaA8(i) => { | ||
let i = DynamicImage::ImageLumaA8(i).into_rgba8(); | ||
width = i.width(); | ||
height = i.height(); | ||
format = if is_srgb { | ||
TextureFormat::Rgba8UnormSrgb | ||
} else { | ||
TextureFormat::Rgba8Unorm | ||
}; | ||
|
||
data = i.into_raw(); | ||
format = TextureFormat::R8Unorm; | ||
data = ConvertBuffer::<ImageBuffer<Luma<u8>, Vec<u8>>>::convert(&i).into_raw(); | ||
} | ||
DynamicImage::ImageRgb8(i) => { | ||
let i = DynamicImage::ImageRgb8(i).into_rgba8(); | ||
width = i.width(); | ||
height = i.height(); | ||
format = if is_srgb { | ||
TextureFormat::Rgba8UnormSrgb | ||
if is_srgb { | ||
format = TextureFormat::Rgba8UnormSrgb; | ||
} else { | ||
TextureFormat::Rgba8Unorm | ||
}; | ||
|
||
data = i.into_raw(); | ||
format = TextureFormat::Rgba8Unorm; | ||
} | ||
data = ConvertBuffer::<ImageBuffer<Rgba<u8>, Vec<u8>>>::convert(&i).into_raw(); | ||
} | ||
DynamicImage::ImageRgba8(i) => { | ||
width = i.width(); | ||
height = i.height(); | ||
format = if is_srgb { | ||
TextureFormat::Rgba8UnormSrgb | ||
if is_srgb { | ||
format = TextureFormat::Rgba8UnormSrgb; | ||
} else { | ||
TextureFormat::Rgba8Unorm | ||
}; | ||
|
||
data = i.into_raw(); | ||
} | ||
DynamicImage::ImageLuma16(i) => { | ||
width = i.width(); | ||
height = i.height(); | ||
format = TextureFormat::R16Uint; | ||
|
||
let raw_data = i.into_raw(); | ||
|
||
data = cast_slice(&raw_data).to_owned(); | ||
} | ||
DynamicImage::ImageLumaA16(i) => { | ||
width = i.width(); | ||
height = i.height(); | ||
format = TextureFormat::Rg16Uint; | ||
|
||
let raw_data = i.into_raw(); | ||
|
||
data = cast_slice(&raw_data).to_owned(); | ||
} | ||
DynamicImage::ImageRgb16(image) => { | ||
width = image.width(); | ||
height = image.height(); | ||
format = TextureFormat::Rgba16Uint; | ||
|
||
let mut local_data = | ||
Vec::with_capacity(width as usize * height as usize * format.pixel_size()); | ||
|
||
for pixel in image.into_raw().chunks_exact(3) { | ||
// TODO: use the array_chunks method once stabilised | ||
// https://github.com/rust-lang/rust/issues/74985 | ||
let r = pixel[0]; | ||
let g = pixel[1]; | ||
let b = pixel[2]; | ||
let a = u16::max_value(); | ||
|
||
local_data.extend_from_slice(&r.to_ne_bytes()); | ||
local_data.extend_from_slice(&g.to_ne_bytes()); | ||
local_data.extend_from_slice(&b.to_ne_bytes()); | ||
local_data.extend_from_slice(&a.to_ne_bytes()); | ||
} | ||
|
||
data = local_data; | ||
} | ||
DynamicImage::ImageRgba16(i) => { | ||
width = i.width(); | ||
height = i.height(); | ||
format = TextureFormat::Rgba16Uint; | ||
|
||
let raw_data = i.into_raw(); | ||
|
||
data = cast_slice(&raw_data).to_owned(); | ||
} | ||
DynamicImage::ImageRgb32F(image) => { | ||
width = image.width(); | ||
height = image.height(); | ||
format = TextureFormat::Rgba32Float; | ||
|
||
let mut local_data = | ||
Vec::with_capacity(width as usize * height as usize * format.pixel_size()); | ||
|
||
for pixel in image.into_raw().chunks_exact(3) { | ||
// TODO: use the array_chunks method once stabilised | ||
// https://github.com/rust-lang/rust/issues/74985 | ||
let r = pixel[0]; | ||
let g = pixel[1]; | ||
let b = pixel[2]; | ||
let a = 1f32; | ||
|
||
local_data.extend_from_slice(&r.to_ne_bytes()); | ||
local_data.extend_from_slice(&g.to_ne_bytes()); | ||
local_data.extend_from_slice(&b.to_ne_bytes()); | ||
local_data.extend_from_slice(&a.to_ne_bytes()); | ||
format = TextureFormat::Rgba8Unorm; | ||
} | ||
|
||
data = local_data; | ||
} | ||
DynamicImage::ImageRgba32F(image) => { | ||
width = image.width(); | ||
height = image.height(); | ||
format = TextureFormat::Rgba32Float; | ||
|
||
let raw_data = image.into_raw(); | ||
|
||
data = cast_slice(&raw_data).to_owned(); | ||
data = i.into_raw(); | ||
} | ||
// DynamicImage is now non exhaustive, catch future variants and convert them | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why remove this line? This is still true as far as I am aware. |
||
_ => { | ||
let image = dyn_img.into_rgba8(); | ||
width = image.width(); | ||
height = image.height(); | ||
format = TextureFormat::Rgba8UnormSrgb; | ||
if is_srgb { | ||
format = TextureFormat::Rgba8UnormSrgb; | ||
} else { | ||
format = TextureFormat::Rgba8Unorm; | ||
} | ||
|
||
data = image.into_raw(); | ||
} | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you explain why you removed support for the following image formats:
Luma16
,LumaA16
,Rgb16
,Rgba16
, and 32F variants?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for not providing enough context and information at the first time.
I'm creating a 3D asset management tool based on bevy, it should be able to load and preview gltf 2.0 model at runtime, so the stability and compatibility comes to first priority.
I've tested the compatibility with all kinds of gltf, including all models from (https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0) and models I collected which will cause the crash issue #7005, and current code will provide maximum stability and compatibility, means all the textures could be loaded and rendered without any problem.
I think, we could build a solid ground without any crash at first, and extend the compatibility case-by-case in future, rather than support as much as possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about custom shaders? Bevy users might want to load any image formats for their own shaders. Even if it doesn't work with the default PBR shader