Skip to content

Commit

Permalink
Split off bevy_image from bevy_render (#15650)
Browse files Browse the repository at this point in the history
# Objective

- bevy_render is gargantuan

## Solution

- Split off bevy_image

## Testing

- Ran some examples
  • Loading branch information
atlv24 authored Oct 4, 2024
1 parent 53adcd7 commit 8b0388c
Show file tree
Hide file tree
Showing 20 changed files with 293 additions and 209 deletions.
1 change: 1 addition & 0 deletions crates/bevy_asset/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ atomicow = "1.0"
async-broadcast = "0.5"
async-fs = "2.0"
async-lock = "3.0"
bitflags = { version = "2.3", features = ["serde"] }
crossbeam-channel = "0.5"
downcast-rs = "1.2"
disqualified = "1.0"
Expand Down
2 changes: 2 additions & 0 deletions crates/bevy_asset/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ mod loader;
mod loader_builders;
mod path;
mod reflect;
mod render_asset;
mod server;

pub use assets::*;
Expand All @@ -192,6 +193,7 @@ pub use loader_builders::{
};
pub use path::*;
pub use reflect::*;
pub use render_asset::*;
pub use server::*;

/// Rusty Object Notation, a crate used to serialize and deserialize bevy assets.
Expand Down
49 changes: 49 additions & 0 deletions crates/bevy_asset/src/render_asset.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize};
use serde::{Deserialize, Serialize};

bitflags::bitflags! {
/// Defines where the asset will be used.
///
/// If an asset is set to the `RENDER_WORLD` but not the `MAIN_WORLD`, the asset will be
/// unloaded from the asset server once it's been extracted and prepared in the render world.
///
/// Unloading the asset saves on memory, as for most cases it is no longer necessary to keep
/// it in RAM once it's been uploaded to the GPU's VRAM. However, this means you can no longer
/// access the asset from the CPU (via the `Assets<T>` resource) once unloaded (without re-loading it).
///
/// If you never need access to the asset from the CPU past the first frame it's loaded on,
/// or only need very infrequent access, then set this to `RENDER_WORLD`. Otherwise, set this to
/// `RENDER_WORLD | MAIN_WORLD`.
///
/// If you have an asset that doesn't actually need to end up in the render world, like an Image
/// that will be decoded into another Image asset, use `MAIN_WORLD` only.
///
/// ## Platform-specific
///
/// On Wasm, it is not possible for now to free reserved memory. To control memory usage, load assets
/// in sequence and unload one before loading the next. See this
/// [discussion about memory management](https://github.com/WebAssembly/design/issues/1397) for more
/// details.
#[repr(transparent)]
#[derive(Serialize, Deserialize, Hash, Clone, Copy, PartialEq, Eq, Debug, Reflect)]
#[reflect(opaque)]
#[reflect(Serialize, Deserialize, Hash, PartialEq, Debug)]
pub struct RenderAssetUsages: u8 {
const MAIN_WORLD = 1 << 0;
const RENDER_WORLD = 1 << 1;
}
}

impl Default for RenderAssetUsages {
/// Returns the default render asset usage flags:
/// `RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD`
///
/// This default configuration ensures the asset persists in the main world, even after being prepared for rendering.
///
/// If your asset does not change, consider using `RenderAssetUsages::RENDER_WORLD` exclusively. This will cause
/// the asset to be unloaded from the main world once it has been prepared for rendering. If the asset does not need
/// to reach the render world at all, use `RenderAssetUsages::MAIN_WORLD` exclusively.
fn default() -> Self {
RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD
}
}
61 changes: 61 additions & 0 deletions crates/bevy_image/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
[package]
name = "bevy_image"
version = "0.15.0-dev"
edition = "2021"
description = "Provides image types for Bevy Engine"
homepage = "https://bevyengine.org"
repository = "https://github.com/bevyengine/bevy"
license = "MIT OR Apache-2.0"
keywords = ["bevy"]

[features]
png = ["image/png"]
exr = ["image/exr"]
hdr = ["image/hdr"]
tga = ["image/tga"]
jpeg = ["image/jpeg"]
bmp = ["image/bmp"]
webp = ["image/webp"]
dds = ["ddsfile"]
pnm = ["image/pnm"]

# For ktx2 supercompression
zlib = ["flate2"]
zstd = ["ruzstd"]

[dependencies]
bevy_asset = { path = "../bevy_asset", version = "0.15.0-dev" }
bevy_color = { path = "../bevy_color", version = "0.15.0-dev", features = [
"serialize",
"wgpu-types",
] }
bevy_math = { path = "../bevy_math", version = "0.15.0-dev" }
bevy_reflect = { path = "../bevy_reflect", version = "0.15.0-dev", features = [
"bevy",
] }
bevy_utils = { path = "../bevy_utils", version = "0.15.0-dev" }

# rendering
image = { version = "0.25.2", default-features = false }

# misc
bitflags = { version = "2.3", features = ["serde"] }
bytemuck = { version = "1.5" }
wgpu = { version = "22", default-features = false }
serde = { version = "1", features = ["derive"] }
thiserror = "1.0"
futures-lite = "2.0.1"
ddsfile = { version = "0.5.2", optional = true }
ktx2 = { version = "0.3.0", optional = true }
# For ktx2 supercompression
flate2 = { version = "1.0.22", optional = true }
ruzstd = { version = "0.7.0", optional = true }
# For transcoding of UASTC/ETC1S universal formats, and for .basis file support
basis-universal = { version = "0.3.0", optional = true }

[lints]
workspace = true

[package.metadata.docs.rs]
rustdoc-args = ["-Zunstable-options", "--generate-link-to-definition"]
all-features = true
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ pub fn dds_format_to_texture_format(
mod test {
use wgpu::{util::TextureDataOrder, TextureDescriptor, TextureDimension};

use crate::texture::CompressedImageFormats;
use crate::CompressedImageFormats;

use super::dds_buffer_to_image;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
use crate::{
render_asset::RenderAssetUsages,
texture::{Image, TextureFormatPixelInfo},
};
use bevy_asset::{io::Reader, AssetLoader, LoadContext};
use crate::{Image, TextureFormatPixelInfo};
use bevy_asset::{io::Reader, AssetLoader, LoadContext, RenderAssetUsages};
use image::ImageDecoder;
use serde::{Deserialize, Serialize};
use thiserror::Error;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
use crate::{
render_asset::RenderAssetUsages,
texture::{Image, TextureFormatPixelInfo},
};
use crate::{Image, TextureFormatPixelInfo};
use bevy_asset::RenderAssetUsages;
use bevy_asset::{io::Reader, AssetLoader, LoadContext};
use image::DynamicImage;
use serde::{Deserialize, Serialize};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,22 @@ use super::dds::*;
#[cfg(feature = "ktx2")]
use super::ktx2::*;

use crate::{
render_asset::{PrepareAssetError, RenderAsset, RenderAssetUsages},
render_resource::{Sampler, Texture, TextureView},
renderer::{RenderDevice, RenderQueue},
texture::BevyDefault,
};
use bevy_asset::Asset;
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::system::{lifetimeless::SRes, Resource, SystemParamItem};
use bevy_asset::{Asset, RenderAssetUsages};
use bevy_math::{AspectRatio, UVec2, Vec2};
use bevy_reflect::prelude::*;
use core::hash::Hash;
use bevy_reflect::Reflect;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use wgpu::{Extent3d, TextureDimension, TextureFormat, TextureViewDescriptor};
pub trait BevyDefault {
fn bevy_default() -> Self;
}

impl BevyDefault for TextureFormat {
fn bevy_default() -> Self {
TextureFormat::Rgba8UnormSrgb
}
}

pub const TEXTURE_ASSET_INDEX: u64 = 0;
pub const SAMPLER_ASSET_INDEX: u64 = 1;
Expand Down Expand Up @@ -180,11 +181,11 @@ pub struct Image {
}

/// Used in [`Image`], this determines what image sampler to use when rendering. The default setting,
/// [`ImageSampler::Default`], will read the sampler from the [`ImagePlugin`](super::ImagePlugin) at setup.
/// [`ImageSampler::Default`], will read the sampler from the `ImagePlugin` at setup.
/// Setting this to [`ImageSampler::Descriptor`] will override the global default descriptor for this [`Image`].
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub enum ImageSampler {
/// Default image sampler, derived from the [`ImagePlugin`](super::ImagePlugin) setup.
/// Default image sampler, derived from the `ImagePlugin` setup.
#[default]
Default,
/// Custom sampler for this image which will override global default.
Expand Down Expand Up @@ -222,14 +223,6 @@ impl ImageSampler {
}
}

/// A rendering resource for the default image sampler which is set during renderer
/// initialization.
///
/// The [`ImagePlugin`](super::ImagePlugin) can be set during app initialization to change the default
/// image sampler.
#[derive(Resource, Debug, Clone, Deref, DerefMut)]
pub struct DefaultImageSampler(pub(crate) Sampler);

/// How edges should be handled in texture addressing.
///
/// See [`ImageSamplerDescriptor`] for information how to configure this.
Expand Down Expand Up @@ -323,9 +316,9 @@ pub enum ImageSamplerBorderColor {
Zero,
}

/// Indicates to an [`ImageLoader`](super::ImageLoader) how an [`Image`] should be sampled.
/// Indicates to an `ImageLoader` how an [`Image`] should be sampled.
///
/// As this type is part of the [`ImageLoaderSettings`](super::ImageLoaderSettings),
/// As this type is part of the `ImageLoaderSettings`,
/// it will be serialized to an image asset `.meta` file which might require a migration in case of
/// a breaking change.
///
Expand Down Expand Up @@ -377,7 +370,7 @@ impl Default for ImageSamplerDescriptor {
}

impl ImageSamplerDescriptor {
/// Returns a sampler descriptor with [`Linear`](crate::render_resource::FilterMode::Linear) min and mag filters
/// Returns a sampler descriptor with [`Linear`](ImageFilterMode::Linear) min and mag filters
#[inline]
pub fn linear() -> ImageSamplerDescriptor {
ImageSamplerDescriptor {
Expand All @@ -388,7 +381,7 @@ impl ImageSamplerDescriptor {
}
}

/// Returns a sampler descriptor with [`Nearest`](crate::render_resource::FilterMode::Nearest) min and mag filters
/// Returns a sampler descriptor with [`Nearest`](ImageFilterMode::Nearest) min and mag filters
#[inline]
pub fn nearest() -> ImageSamplerDescriptor {
ImageSamplerDescriptor {
Expand Down Expand Up @@ -924,75 +917,6 @@ impl TextureFormatPixelInfo for TextureFormat {
}
}

/// The GPU-representation of an [`Image`].
/// Consists of the [`Texture`], its [`TextureView`] and the corresponding [`Sampler`], and the texture's size.
#[derive(Debug, Clone)]
pub struct GpuImage {
pub texture: Texture,
pub texture_view: TextureView,
pub texture_format: TextureFormat,
pub sampler: Sampler,
pub size: UVec2,
pub mip_level_count: u32,
}

impl RenderAsset for GpuImage {
type SourceAsset = Image;
type Param = (
SRes<RenderDevice>,
SRes<RenderQueue>,
SRes<DefaultImageSampler>,
);

#[inline]
fn asset_usage(image: &Self::SourceAsset) -> RenderAssetUsages {
image.asset_usage
}

#[inline]
fn byte_len(image: &Self::SourceAsset) -> Option<usize> {
Some(image.data.len())
}

/// Converts the extracted image into a [`GpuImage`].
fn prepare_asset(
image: Self::SourceAsset,
(render_device, render_queue, default_sampler): &mut SystemParamItem<Self::Param>,
) -> Result<Self, PrepareAssetError<Self::SourceAsset>> {
let texture = render_device.create_texture_with_data(
render_queue,
&image.texture_descriptor,
// TODO: Is this correct? Do we need to use `MipMajor` if it's a ktx2 file?
wgpu::util::TextureDataOrder::default(),
&image.data,
);

let size = image.size();
let texture_view = texture.create_view(
image
.texture_view_descriptor
.or_else(|| Some(TextureViewDescriptor::default()))
.as_ref()
.unwrap(),
);
let sampler = match image.sampler {
ImageSampler::Default => (***default_sampler).clone(),
ImageSampler::Descriptor(descriptor) => {
render_device.create_sampler(&descriptor.as_wgpu())
}
};

Ok(GpuImage {
texture,
texture_view,
texture_format: image.texture_descriptor.format,
sampler,
size,
mip_level_count: image.texture_descriptor.mip_level_count,
})
}
}

bitflags::bitflags! {
#[derive(Default, Clone, Copy, Eq, PartialEq, Debug)]
#[repr(transparent)]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
use crate::{
render_asset::RenderAssetUsages,
texture::{Image, TextureFormatPixelInfo},
};
use crate::{Image, TextureFormatPixelInfo};
use bevy_asset::RenderAssetUsages;
use image::{DynamicImage, ImageBuffer};
use thiserror::Error;
use wgpu::{Extent3d, TextureDimension, TextureFormat};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1496,7 +1496,7 @@ pub fn ktx2_format_to_texture_format(

#[cfg(test)]
mod tests {
use crate::texture::CompressedImageFormats;
use crate::CompressedImageFormats;

use super::ktx2_buffer_to_image;

Expand Down
28 changes: 28 additions & 0 deletions crates/bevy_image/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// FIXME(15321): solve CI failures, then replace with `#![expect()]`.
#![allow(missing_docs, reason = "Not all docs are written yet, see #3492.")]
#![allow(unsafe_code)]

mod image;
pub use self::image::*;
#[cfg(feature = "basis-universal")]
mod basis;
#[cfg(feature = "dds")]
mod dds;
#[cfg(feature = "exr")]
mod exr_texture_loader;
#[cfg(feature = "hdr")]
mod hdr_texture_loader;
#[cfg(feature = "ktx2")]
mod ktx2;

#[cfg(feature = "ktx2")]
pub use self::ktx2::*;
#[cfg(feature = "dds")]
pub use dds::*;
#[cfg(feature = "exr")]
pub use exr_texture_loader::*;
#[cfg(feature = "hdr")]
pub use hdr_texture_loader::*;

pub(crate) mod image_texture_conversion;
pub use image_texture_conversion::IntoDynamicImageError;
Loading

0 comments on commit 8b0388c

Please sign in to comment.