From 9ab41d06b4ff0b52360a8cda756a3b1343a1e2fc Mon Sep 17 00:00:00 2001 From: HampusM Date: Thu, 26 Mar 2026 16:42:22 +0100 Subject: fix(engine): prevent uploading textures with invalid row alignments --- engine/src/image.rs | 25 ++++++++++++++++++++ engine/src/renderer/opengl.rs | 55 ++++++++++++++++++++++++++++++++----------- engine/src/texture.rs | 6 ++++- 3 files changed, 71 insertions(+), 15 deletions(-) (limited to 'engine') diff --git a/engine/src/image.rs b/engine/src/image.rs index cf31a92..ac95f22 100644 --- a/engine/src/image.rs +++ b/engine/src/image.rs @@ -50,6 +50,26 @@ impl Image } } + pub fn from_color_and_alpha( + dimens: impl Into>, + color: impl Into>, + alpha: u8, + ) -> Self + { + let dimens: Dimens = dimens.into(); + + let color: Color = color.into(); + + Self { + inner: image_rs::RgbaImage::from_pixel( + dimens.width, + dimens.height, + image_rs::Rgba([color.red, color.green, color.blue, alpha]), + ) + .into(), + } + } + pub fn dimensions(&self) -> Dimens { self.inner.dimensions().into() @@ -68,6 +88,11 @@ impl Image } } + pub fn to_rgba8(&self) -> Self + { + Self { inner: self.inner.to_rgba8().into() } + } + pub fn as_bytes(&self) -> &[u8] { self.inner.as_bytes() diff --git a/engine/src/renderer/opengl.rs b/engine/src/renderer/opengl.rs index 11a4ca9..dc8e561 100644 --- a/engine/src/renderer/opengl.rs +++ b/engine/src/renderer/opengl.rs @@ -61,7 +61,7 @@ use opengl_bindings::{ContextWithFns, CurrentContextWithFns}; use safer_ffi::layout::ReprC; use zerocopy::{Immutable, IntoBytes}; -use crate::asset::{Assets, Id as AssetId}; +use crate::asset::{Assets, Handle as AssetHandle}; use crate::data_types::dimens::Dimens; use crate::image::{ColorType as ImageColorType, Image}; use crate::matrix::Matrix; @@ -100,6 +100,7 @@ use crate::shader::{ use crate::texture::{ Filtering as TextureFiltering, Properties as TextureProperties, + Texture, Wrapping as TextureWrapping, }; use crate::vector::{Vec2, Vec3}; @@ -898,17 +899,11 @@ fn handle_commands( continue; }; - let Some(texture) = assets.get(&texture_asset) else { - tracing::error!("Texture asset is not loaded",); - continue; - }; - if let Err(err) = create_texture_object( curr_gl_ctx, &mut renderer_object_store, - texture_asset.id(), - &texture.image, - &texture.properties, + &assets, + &texture_asset, ) { tracing::error!("Failed to create texture object: {err}"); } @@ -1058,12 +1053,11 @@ fn handle_commands( fn create_texture_object( curr_gl_ctx: &CurrentContextWithFns<'_>, renderer_object_store: &mut RendererObjectStore, - texture_image_asset_id: AssetId, - image: &Image, - texture_properties: &TextureProperties, + assets: &Assets, + texture_asset: &AssetHandle, ) -> Result<(), GlTextureGenerateError> { - let object_id = RendererObjectId::Asset(texture_image_asset_id); + let object_id = RendererObjectId::Asset(texture_asset.id()); if renderer_object_store.contains_with_id(&object_id) { tracing::error!( @@ -1073,10 +1067,43 @@ fn create_texture_object( return Ok(()); } + let Some(texture) = assets.get(&texture_asset) else { + tracing::error!("Texture asset is not loaded",); + return Ok(()); + }; + + let texture_image = match texture.image.color_type() { + ImageColorType::Rgb8 if (texture.image.dimensions().width * 3) % 4 != 0 => { + // The texture will be corrupted if the alignment of each horizontal line of + // the texture pixel array is not multiple of 4. + // + // Read more about this at + // wikis.khronos.org/opengl/Common_Mistakes#Texture_upload_and_pixel_reads + // + // To prevent this, the image is converted to RGBA8. RGBA8 images have a pixel + // size of 4 bytes so they cannot have any alignment problems + + tracing::warn!( + texture_asset = %assets + .get_label(&texture_asset) + .expect("Not possible"), + concat!( + "Converting texture image from RGB8 to RGBA8 to prevent alignment ", + "problems. This conversion may be slow. Consider changing the ", + "texture image's pixel format to RGBA8" + ) + ); + + &texture.image.to_rgba8() + } + _ => &texture.image, + }; + renderer_object_store.insert( object_id, RendererObject::from_raw( - create_gl_texture(curr_gl_ctx, image, texture_properties)?.into_raw(), + create_gl_texture(curr_gl_ctx, texture_image, &texture.properties)? + .into_raw(), RendererObjectKind::Texture, ), ); diff --git a/engine/src/texture.rs b/engine/src/texture.rs index b92bc2e..b069228 100644 --- a/engine/src/texture.rs +++ b/engine/src/texture.rs @@ -111,7 +111,11 @@ pub(crate) fn initialize(assets: &mut Assets) assets.store_with_label( WHITE_1X1_ASSET_LABEL.clone(), Texture { - image: Image::from_color(Dimens { width: 1, height: 1 }, Color::WHITE_U8), + image: Image::from_color_and_alpha( + Dimens { width: 1, height: 1 }, + Color::WHITE_U8, + 1, + ), properties: Properties::default(), }, ); -- cgit v1.2.3-18-g5258