summaryrefslogtreecommitdiff
path: root/engine/src/rendering
diff options
context:
space:
mode:
Diffstat (limited to 'engine/src/rendering')
-rw-r--r--engine/src/rendering/backend/opengl.rs295
-rw-r--r--engine/src/rendering/main_render_pass.rs170
2 files changed, 155 insertions, 310 deletions
diff --git a/engine/src/rendering/backend/opengl.rs b/engine/src/rendering/backend/opengl.rs
index 755388c..4d2a74f 100644
--- a/engine/src/rendering/backend/opengl.rs
+++ b/engine/src/rendering/backend/opengl.rs
@@ -6,7 +6,6 @@ use std::hint::cold_path;
use std::num::NonZero;
use ecs::query::term::With;
-use ecs::util::BorrowedOrOwned;
use glutin::config::Config as GlutinConfig;
use glutin::display::GetGlDisplay;
use glutin::error::Error as GlutinError;
@@ -77,7 +76,6 @@ use crate::ecs::actions::Actions;
use crate::ecs::query::term::Without;
use crate::ecs::sole::Single;
use crate::ecs::{Component, Query, Sole};
-use crate::image::{ColorType as ImageColorType, Image};
use crate::reflection::EnumReflectionExt;
use crate::rendering::backend::opengl::glutin_compat::{
DisplayBuilder,
@@ -117,6 +115,7 @@ use crate::rendering::{
Surface,
SurfaceId,
TargetWindow,
+ TextureCreation,
TexturePixelDataFormat,
TextureUpdate,
POST_RENDER_PHASE,
@@ -126,8 +125,6 @@ use crate::texture::{
CubeMapFace as CubeMapTextureFace,
Filtering as TextureFiltering,
Properties as TextureProperties,
- TexCubeMap,
- Texture,
Wrapping as TextureWrapping,
};
use crate::util::{MapVec, OptionExt};
@@ -364,7 +361,7 @@ enum BackendResource
{
texture: GlTexture,
pixel_data_format: TexturePixelDataFormat,
- metadata: TextureKind,
+ kind: TextureKind,
},
}
@@ -953,22 +950,39 @@ fn handle_commands(
)
.unwrap();
}
- Command::CreateTexture(object_id, texture) => {
- let _ = backend_resources.try_create_resource::<()>(
- object_store,
- object_id,
- || {
- let (gl_texture, texture_pixel_data_format, texture_kind) =
- create_texture_object(gl_context, assets, texture)
- .map_err(|_| ())?;
-
- Ok(BackendResource::Texture {
- texture: gl_texture,
- pixel_data_format: texture_pixel_data_format,
- metadata: texture_kind,
- })
- },
- );
+ Command::CreateTexture {
+ obj_id,
+ pixel_data_format,
+ creation,
+ properties,
+ } => {
+ let kind = match &creation {
+ TextureCreation::Texture2D { .. } => TextureKind::Texture2D,
+ TextureCreation::CubeMap { .. } => TextureKind::CubeMap,
+ };
+
+ if let Err(err) = backend_resources
+ .try_create_resource::<opengl_bindings::texture::Error>(
+ object_store,
+ obj_id,
+ || {
+ let gl_texture = create_gl_texture(
+ gl_context,
+ creation,
+ pixel_data_format,
+ &properties,
+ )?;
+
+ Ok(BackendResource::Texture {
+ texture: gl_texture,
+ pixel_data_format,
+ kind,
+ })
+ },
+ )
+ {
+ tracing::error!("Failed to create texture: {err}");
+ }
}
Command::UpdateTexture {
obj_id,
@@ -987,7 +1001,7 @@ fn handle_commands(
let BackendResource::Texture {
texture: gl_texture,
pixel_data_format: tex_pixel_data_format,
- metadata: tex_metadata,
+ kind: tex_metadata,
} = texture_resource
else {
unreachable!();
@@ -1298,176 +1312,6 @@ enum CreateGlContextError
MakeContextCurrent(#[source] GlMakeContextCurrentError),
}
-enum TextureCreationData<'a>
-{
- Texture2D
- {
- image: BorrowedOrOwned<'a, Image>
- },
- CubeMap
- {
- images: [(CubeMapTextureFace, BorrowedOrOwned<'a, Image>); 6],
- color_type: ImageColorType,
- is_srgb: bool,
- size: Dimens<u32>,
- },
-}
-
-fn validate_cube_map_texture(texture: &TexCubeMap) -> bool
-{
- let first_image_color_type = texture.images[0].1.color_type();
-
- if !texture.images[1..]
- .into_iter()
- .all(|(_, image)| image.color_type() == first_image_color_type)
- {
- tracing::error!("All texture images must have same color type");
- return false;
- }
-
- let first_image_size = texture.images[0].1.dimensions();
-
- if !texture.images[1..]
- .into_iter()
- .all(|(_, image)| image.dimensions() == first_image_size)
- {
- tracing::error!("All texture images must have same dimensions");
- return false;
- }
-
- let first_image_is_srgb = texture.images[0].1.color_space_is_srgb();
-
- if !texture.images[1..]
- .into_iter()
- .all(|(_, image)| image.color_space_is_srgb() == first_image_is_srgb)
- {
- tracing::error!("All texture images must have same color space");
- return false;
- }
-
- true
-}
-
-fn convert_textures_if_unaligned<const IMAGE_CNT: usize, Extra>(
- images: [(Extra, &Image); IMAGE_CNT],
- color_type: ImageColorType,
- size: Dimens<u32>,
-) -> [(Extra, BorrowedOrOwned<'_, Image>); IMAGE_CNT]
-{
- match color_type {
- ImageColorType::Rgb8 if (size.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
-
- // TODO: Make it clearer in warning log which texture is being talked about
- tracing::warn!(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"
- ));
-
- images.map(|(extra, image)| (extra, BorrowedOrOwned::Owned(image.to_rgba8())))
- }
- _ => images.map(|(extra, image)| (extra, BorrowedOrOwned::Borrowned(image))),
- }
-}
-
-#[tracing::instrument(skip_all)]
-fn create_texture_object(
- curr_gl_ctx: &MaybeCurrentContextWithFns,
- assets: &Assets,
- texture: AssetOrValue<Texture>,
-) -> Result<(GlTexture, TexturePixelDataFormat, TextureKind), ()>
-{
- let texture = match &texture {
- AssetOrValue::Asset(texture_asset) => {
- let Some(texture) = assets.get(&texture_asset) else {
- tracing::error!(
- texture_asset_id=?texture_asset.id(),
- "Texture asset does not exist"
- );
- return Err(());
- };
-
- texture
- }
- AssetOrValue::Value(texture) => texture,
- };
-
- let (texture_things, texture_properties, texture_pixel_data_format, texture_kind) =
- match texture {
- Texture::Texture2D(texture) => {
- let [(_, image)] = convert_textures_if_unaligned(
- [((), &texture.image)],
- texture.image.color_type(),
- texture.image.dimensions(),
- );
-
- let tex_pixel_data_format = TexturePixelDataFormat::for_image(&image)
- .expect("No pixel data format is available for image");
-
- (
- TextureCreationData::Texture2D { image },
- &texture.properties,
- tex_pixel_data_format,
- TextureKind::Texture2D,
- )
- }
- Texture::CubeMap(texture) => {
- if !validate_cube_map_texture(texture) {
- return Err(());
- }
-
- let first_image_color_type = texture.images[0].1.color_type();
- let first_image_size = texture.images[0].1.dimensions();
- let first_image_is_srgb = texture.images[0].1.color_space_is_srgb();
-
- let images = convert_textures_if_unaligned(
- std::array::from_fn::<_, 6, _>(|index| {
- let (face, image) = &texture.images[index];
-
- (*face, image)
- }),
- first_image_color_type,
- first_image_size,
- );
-
- let tex_pixel_data_format =
- TexturePixelDataFormat::for_image(&images[0].1)
- .expect("No pixel data format is available for image");
-
- (
- TextureCreationData::CubeMap {
- images,
- color_type: first_image_color_type,
- is_srgb: first_image_is_srgb,
- size: first_image_size,
- },
- &texture.properties,
- tex_pixel_data_format,
- TextureKind::CubeMap,
- )
- }
- };
-
- let gl_texture =
- match create_gl_texture(curr_gl_ctx, texture_things, texture_properties) {
- Ok(gl_texture) => gl_texture,
- Err(err) => {
- tracing::error!("Failed to create texture object: {err}");
- return Err(());
- }
- };
-
- Ok((gl_texture, texture_pixel_data_format, texture_kind))
-}
-
#[tracing::instrument(skip_all)]
fn update_texture_object(
curr_gl_ctx: &MaybeCurrentContextWithFns,
@@ -1540,12 +1384,6 @@ fn draw_mesh(
Ok(())
}
-struct ImageDataProperties
-{
- color_type: ImageColorType,
- is_srgb: bool,
-}
-
fn tex_pixel_data_format_into_gl(
tex_pixel_data_format: TexturePixelDataFormat,
) -> GlTexturePixelDataFormat
@@ -1582,59 +1420,30 @@ fn tex_pixel_data_format_into_gl(
}
}
-fn create_pixel_data_format(
- image_data_props: ImageDataProperties,
-) -> GlTexturePixelDataFormat
-{
- match image_data_props.color_type {
- ImageColorType::Rgb8 if image_data_props.is_srgb => {
- GlTexturePixelDataFormat::Srgb(
- opengl_bindings::texture::SrgbDataType::UnsignedByte,
- )
- }
- ImageColorType::Rgb8 => GlTexturePixelDataFormat::Rgb(
- opengl_bindings::texture::RgbDataType::UnsignedByte,
- ),
- ImageColorType::Rgba8 if image_data_props.is_srgb => {
- GlTexturePixelDataFormat::Srgba(
- opengl_bindings::texture::SrgbaDataType::UnsignedByte,
- )
- }
- ImageColorType::Rgba8 => GlTexturePixelDataFormat::Rgba(
- opengl_bindings::texture::RgbaDataType::UnsignedByte,
- ),
- _ => {
- unimplemented!();
- }
- }
-}
-
fn create_gl_texture(
curr_gl_context: &MaybeCurrentContextWithFns,
- texture_things: TextureCreationData<'_>,
+ texture_creation: TextureCreation,
+ texture_pixel_data_format: TexturePixelDataFormat,
texture_properties: &TextureProperties,
) -> Result<GlTexture, GlTextureError>
{
- let gl_texture = match texture_things {
- TextureCreationData::Texture2D { image } => GlTexture::builder()
- .size(image.dimensions().into())
- .create_2d(
+ let gl_texture = match texture_creation {
+ TextureCreation::Texture2D { size, image } => {
+ GlTexture::builder().size(size.into()).create_2d(
curr_gl_context,
- Some(image.as_bytes()),
- create_pixel_data_format(ImageDataProperties {
- color_type: image.color_type(),
- is_srgb: image.color_space_is_srgb(),
- }),
- ),
- TextureCreationData::CubeMap { images, color_type, is_srgb, size } => {
+ image.as_deref(),
+ tex_pixel_data_format_into_gl(texture_pixel_data_format),
+ )
+ }
+ TextureCreation::CubeMap { size, images } => {
GlTexture::builder().size(size.into()).create_cube_map(
curr_gl_context,
- Some(std::array::from_fn::<_, 6, _>(|index| {
- let (face, image) = &images[index];
-
- (cube_map_texture_face_to_gl(*face), image.as_bytes())
- })),
- create_pixel_data_format(ImageDataProperties { color_type, is_srgb }),
+ images.as_ref().map(|images| {
+ images.each_ref().map(|(face, image)| {
+ (cube_map_texture_face_to_gl(*face), &**image)
+ })
+ }),
+ tex_pixel_data_format_into_gl(texture_pixel_data_format),
)
}
}?;
diff --git a/engine/src/rendering/main_render_pass.rs b/engine/src/rendering/main_render_pass.rs
index 21c0745..895517b 100644
--- a/engine/src/rendering/main_render_pass.rs
+++ b/engine/src/rendering/main_render_pass.rs
@@ -2,6 +2,7 @@ use std::path::Path;
use std::sync::LazyLock;
use ecs::actions::Actions;
+use ecs::component::local::Local;
use ecs::pair::ChildOf;
use ecs::query::term::{Traverse, TraverseUp};
use ecs::uid::Uid;
@@ -9,11 +10,13 @@ use ecs::Component;
use crate::asset::{Assets, Id as AssetId, Label as AssetLabel};
use crate::camera::{Active as ActiveCamera, Camera};
-use crate::data_types::dimens::Dimens3;
+use crate::color::Rgba;
+use crate::data_types::dimens::{Dimens, Dimens3};
use crate::draw_flags::{DrawFlags, NoDraw, PolygonModeConfig};
use crate::ecs::query::term::{With, Without};
use crate::ecs::sole::Single;
use crate::ecs::Query;
+use crate::image::Image;
use crate::lighting::{
DirectionalLight,
Environmental as EnvironmentalLighting,
@@ -46,18 +49,19 @@ use crate::rendering::{
MeshUsage,
RenderPass,
RenderPasses,
+ RgbaTextureDataType,
Surface,
TargetWindow,
+ TextureCreation,
+ TexturePixelDataFormat,
};
use crate::scene::{Active as ActiveScene, Scene};
use crate::sky_box::SkyBox;
use crate::texture::{
Filtering as TextureFiltering,
Properties as TextureProperties,
- TexCubeMap,
Texture,
Wrapping as TextureWrapping,
- WHITE_1X1_ASSET_LABEL as TEXTURE_WHITE_1X1_ASSET_LABEL,
};
use crate::transform::Transform;
use crate::vector::Vec3;
@@ -92,6 +96,12 @@ struct SkyBoxIds
shader_asset: AssetId,
}
+#[derive(Debug, Default, Component)]
+pub struct MiscObjectIds
+{
+ white_1x1_tex_obj_id: Option<ObjectId>,
+}
+
#[tracing::instrument(skip_all)]
pub fn add_main_render_pass(
renderable_query: Query<RenderableEntity<'_>>,
@@ -123,6 +133,7 @@ pub fn add_main_render_pass(
mut render_passes: Single<RenderPasses>,
mut object_store: Single<ObjectStore>,
mut actions: Actions,
+ mut misc_object_ids: Local<MiscObjectIds>,
) -> Result<(), crate::Error>
{
let assets = assets.get_mut()?;
@@ -170,20 +181,33 @@ pub fn add_main_render_pass(
.commands
.push(Command::MakeCurrent(window_surface.id));
- let default_texture_asset = assets
- .get_handle_to_loaded::<Texture>(TEXTURE_WHITE_1X1_ASSET_LABEL.clone())
- .expect("Not possible");
+ let white_1x1_tex_obj_id =
+ *misc_object_ids.white_1x1_tex_obj_id.get_or_insert_with(|| {
+ let white_1x1_tex_obj_id = ObjectId::new_sequential();
- if !object_store
- .contains_maybe_pending_with_id(&ObjectId::Asset(default_texture_asset.id()))
- {
- object_store.insert_pending(ObjectId::Asset(default_texture_asset.id()));
+ object_store.insert_pending(white_1x1_tex_obj_id);
- render_pass.commands.push(Command::CreateTexture(
- ObjectId::Asset(default_texture_asset.id()),
- AssetOrValue::Asset(default_texture_asset),
- ));
- }
+ render_pass.commands.push(Command::CreateTexture {
+ obj_id: white_1x1_tex_obj_id,
+ pixel_data_format: TexturePixelDataFormat::Rgba(
+ RgbaTextureDataType::UnsignedByte,
+ ),
+ creation: TextureCreation::Texture2D {
+ size: Dimens { width: 1, height: 1 },
+ image: Some(
+ Image::from_color(
+ Rgba::<u8>::white(),
+ Dimens { width: 1, height: 1 },
+ )
+ .into_bytes()
+ .into_boxed_slice(),
+ ),
+ },
+ properties: TextureProperties::default(),
+ });
+
+ white_1x1_tex_obj_id
+ });
let sky_box_ids = if let Some(scene_skybox) = &scene_skybox {
load_sky_box(
@@ -279,16 +303,45 @@ pub fn add_main_render_pass(
.into_iter()
.flatten()
{
- if !object_store
- .contains_maybe_pending_with_id(&ObjectId::Asset(texture_asset.id()))
- {
- object_store.insert_pending(ObjectId::Asset(texture_asset.id()));
-
- render_pass.commands.push(Command::CreateTexture(
- ObjectId::Asset(texture_asset.id()),
- AssetOrValue::Asset(texture_asset.clone()),
- ));
+ let Some(texture) = assets.get(texture_asset) else {
+ continue;
+ };
+
+ let Texture::Texture2D(texture) = texture else {
+ tracing::error!(
+ texture_asset_id = ?texture_asset.id(),
+ texture_asset_label = ?assets.get_label(texture_asset),
+ "Material texture map is not 2D"
+ );
+ continue;
+ };
+
+ let texture_object_id = ObjectId::Asset(texture_asset.id());
+
+ if object_store.contains_maybe_pending_with_id(&texture_object_id) {
+ continue;
}
+
+ let Some(tex_pixel_data_format) =
+ TexturePixelDataFormat::for_image(&texture.image)
+ else {
+ tracing::error!(
+ "No texture pixel data format is available for image"
+ );
+ continue;
+ };
+
+ object_store.insert_pending(texture_object_id);
+
+ render_pass.commands.push(Command::CreateTexture {
+ obj_id: texture_object_id,
+ pixel_data_format: tex_pixel_data_format,
+ creation: TextureCreation::Texture2D {
+ size: texture.image.dimensions(),
+ image: Some(texture.image.as_bytes().to_vec().into_boxed_slice()),
+ },
+ properties: texture.properties.clone(),
+ });
}
add_set_3d_shader_bindings(
@@ -303,8 +356,8 @@ pub fn add_main_render_pass(
&window,
&point_light_query,
&directional_light_query,
- assets,
shader_program,
+ white_1x1_tex_obj_id,
)?;
if let Some(draw_flags) = draw_flags.as_deref().and_then(|draw_flags| {
@@ -448,8 +501,8 @@ fn add_set_3d_shader_bindings(
&DirectionalLight,
Traverse<(With<Scene>, With<ActiveScene>), TraverseUp, ChildOf>,
)>,
- assets: &Assets,
shader_program: &ShaderProgram,
+ white_1x1_tex_obj_id: ObjectId,
) -> Result<(), crate::Error>
{
let (material, material_flags, transform) = renderable;
@@ -524,16 +577,8 @@ fn add_set_3d_shader_bindings(
material
.ambient_map
.as_ref()
- .map(|ambient_map| ambient_map.clone())
- .unwrap_or_else(|| {
- assets
- .get_handle_to_loaded(
- TEXTURE_WHITE_1X1_ASSET_LABEL.clone(),
- )
- .expect("Not possible")
- })
- .id()
- .into(),
+ .map(|ambient_map| ObjectId::Asset(ambient_map.id()))
+ .unwrap_or(white_1x1_tex_obj_id),
ShaderBindingTextureKind::Texture2D,
))?,
shader_cursor
@@ -542,16 +587,8 @@ fn add_set_3d_shader_bindings(
material
.diffuse_map
.as_ref()
- .map(|diffuse_map| diffuse_map.clone())
- .unwrap_or_else(|| {
- assets
- .get_handle_to_loaded(
- TEXTURE_WHITE_1X1_ASSET_LABEL.clone(),
- )
- .expect("Not possible")
- })
- .id()
- .into(),
+ .map(|diffuse_map| ObjectId::Asset(diffuse_map.id()))
+ .unwrap_or(white_1x1_tex_obj_id),
ShaderBindingTextureKind::Texture2D,
))?,
shader_cursor
@@ -560,16 +597,8 @@ fn add_set_3d_shader_bindings(
material
.specular_map
.as_ref()
- .map(|specular_map| specular_map.clone())
- .unwrap_or_else(|| {
- assets
- .get_handle_to_loaded(
- TEXTURE_WHITE_1X1_ASSET_LABEL.clone(),
- )
- .expect("Not possible")
- })
- .id()
- .into(),
+ .map(|specular_map| ObjectId::Asset(specular_map.id()))
+ .unwrap_or(white_1x1_tex_obj_id),
ShaderBindingTextureKind::Texture2D,
))?,
material_shader_cursor
@@ -769,17 +798,24 @@ fn load_sky_box(
object_store.insert_pending(texture_object_id);
- render_pass.commands.push(Command::CreateTexture(
- texture_object_id,
- AssetOrValue::Value(Texture::CubeMap(TexCubeMap {
- images: part_images.map(|(face, image)| (face, image.clone())),
- properties: TextureProperties::builder()
- .minifying_filter(TextureFiltering::Linear)
- .magnifying_filter(TextureFiltering::Linear)
- .wrap(TextureWrapping::ClampToEdge)
- .build(),
- })),
- ));
+ render_pass.commands.push(Command::CreateTexture {
+ obj_id: texture_object_id,
+ pixel_data_format: TexturePixelDataFormat::for_image(
+ part_images[0].1,
+ )
+ .expect("No texture pixel data format is available for image"),
+ creation: TextureCreation::CubeMap {
+ size: part_images[0].1.dimensions(),
+ images: Some(part_images.map(|(face, image)| {
+ (face, image.as_bytes().to_vec().into_boxed_slice())
+ })),
+ },
+ properties: TextureProperties::builder()
+ .minifying_filter(TextureFiltering::Linear)
+ .magnifying_filter(TextureFiltering::Linear)
+ .wrap(TextureWrapping::ClampToEdge)
+ .build(),
+ });
texture_object_id
}