bevy_render/texture/
mod.rs

1mod fallback_image;
2mod gpu_image;
3mod texture_attachment;
4mod texture_cache;
5
6pub use crate::render_resource::DefaultImageSampler;
7#[cfg(feature = "basis-universal")]
8use bevy_image::CompressedImageSaver;
9#[cfg(feature = "hdr")]
10use bevy_image::HdrTextureLoader;
11use bevy_image::{CompressedImageFormats, Image, ImageLoader, ImageSamplerDescriptor};
12pub use fallback_image::*;
13pub use gpu_image::*;
14pub use texture_attachment::*;
15pub use texture_cache::*;
16
17use crate::{
18    render_asset::RenderAssetPlugin, renderer::RenderDevice, Render, RenderApp, RenderSet,
19};
20use bevy_app::{App, Plugin};
21use bevy_asset::{AssetApp, Assets, Handle};
22use bevy_ecs::prelude::*;
23
24/// A handle to a 1 x 1 transparent white image.
25///
26/// Like [`Handle<Image>::default`], this is a handle to a fallback image asset.
27/// While that handle points to an opaque white 1 x 1 image, this handle points to a transparent 1 x 1 white image.
28// Number randomly selected by fair WolframAlpha query. Totally arbitrary.
29pub const TRANSPARENT_IMAGE_HANDLE: Handle<Image> =
30    Handle::weak_from_u128(154728948001857810431816125397303024160);
31
32// TODO: replace Texture names with Image names?
33/// Adds the [`Image`] as an asset and makes sure that they are extracted and prepared for the GPU.
34pub struct ImagePlugin {
35    /// The default image sampler to use when [`bevy_image::ImageSampler`] is set to `Default`.
36    pub default_sampler: ImageSamplerDescriptor,
37}
38
39impl Default for ImagePlugin {
40    fn default() -> Self {
41        ImagePlugin::default_linear()
42    }
43}
44
45impl ImagePlugin {
46    /// Creates image settings with linear sampling by default.
47    pub fn default_linear() -> ImagePlugin {
48        ImagePlugin {
49            default_sampler: ImageSamplerDescriptor::linear(),
50        }
51    }
52
53    /// Creates image settings with nearest sampling by default.
54    pub fn default_nearest() -> ImagePlugin {
55        ImagePlugin {
56            default_sampler: ImageSamplerDescriptor::nearest(),
57        }
58    }
59}
60
61impl Plugin for ImagePlugin {
62    fn build(&self, app: &mut App) {
63        #[cfg(feature = "exr")]
64        {
65            app.init_asset_loader::<bevy_image::ExrTextureLoader>();
66        }
67
68        #[cfg(feature = "hdr")]
69        {
70            app.init_asset_loader::<HdrTextureLoader>();
71        }
72
73        app.add_plugins(RenderAssetPlugin::<GpuImage>::default())
74            .register_type::<Image>()
75            .init_asset::<Image>()
76            .register_asset_reflect::<Image>();
77
78        let mut image_assets = app.world_mut().resource_mut::<Assets<Image>>();
79
80        image_assets.insert(&Handle::default(), Image::default());
81        image_assets.insert(&TRANSPARENT_IMAGE_HANDLE, Image::transparent());
82
83        #[cfg(feature = "basis-universal")]
84        if let Some(processor) = app
85            .world()
86            .get_resource::<bevy_asset::processor::AssetProcessor>()
87        {
88            processor.register_processor::<bevy_asset::processor::LoadTransformAndSave<
89                ImageLoader,
90                bevy_asset::transformer::IdentityAssetTransformer<Image>,
91                CompressedImageSaver,
92            >>(CompressedImageSaver.into());
93            processor.set_default_processor::<bevy_asset::processor::LoadTransformAndSave<
94                ImageLoader,
95                bevy_asset::transformer::IdentityAssetTransformer<Image>,
96                CompressedImageSaver,
97            >>("png");
98        }
99
100        if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
101            render_app.init_resource::<TextureCache>().add_systems(
102                Render,
103                update_texture_cache_system.in_set(RenderSet::Cleanup),
104            );
105        }
106
107        if !ImageLoader::SUPPORTED_FILE_EXTENSIONS.is_empty() {
108            app.preregister_asset_loader::<ImageLoader>(ImageLoader::SUPPORTED_FILE_EXTENSIONS);
109        }
110    }
111
112    fn finish(&self, app: &mut App) {
113        if !ImageLoader::SUPPORTED_FORMATS.is_empty() {
114            let supported_compressed_formats = match app.world().get_resource::<RenderDevice>() {
115                Some(render_device) => {
116                    CompressedImageFormats::from_features(render_device.features())
117                }
118                None => CompressedImageFormats::NONE,
119            };
120            app.register_asset_loader(ImageLoader::new(supported_compressed_formats));
121        }
122
123        if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
124            let default_sampler = {
125                let device = render_app.world().resource::<RenderDevice>();
126                device.create_sampler(&self.default_sampler.as_wgpu())
127            };
128            render_app
129                .insert_resource(DefaultImageSampler(default_sampler))
130                .init_resource::<FallbackImage>()
131                .init_resource::<FallbackImageZero>()
132                .init_resource::<FallbackImageCubemap>()
133                .init_resource::<FallbackImageFormatMsaaCache>();
134        }
135    }
136}