summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2026-03-26 16:42:22 +0100
committerHampusM <hampus@hampusmat.com>2026-03-26 16:43:34 +0100
commit9ab41d06b4ff0b52360a8cda756a3b1343a1e2fc (patch)
tree5a2be512270ec6a2480b045f218b7a0ba54e00d7
parent3a92f3c8eacc4b5b51bf31280032113d9840ea03 (diff)
fix(engine): prevent uploading textures with invalid row alignmentsHEADmaster
-rw-r--r--engine/src/image.rs25
-rw-r--r--engine/src/renderer/opengl.rs55
-rw-r--r--engine/src/texture.rs6
3 files changed, 71 insertions, 15 deletions
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<Dimens<u32>>,
+ color: impl Into<Color<u8>>,
+ alpha: u8,
+ ) -> Self
+ {
+ let dimens: Dimens<u32> = dimens.into();
+
+ let color: Color<u8> = 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<u32>
{
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<Texture>,
) -> 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(),
},
);