Path: blob/main/crates/bevy_render/src/texture/fallback_image.rs
6596 views
use crate::{1render_resource::*,2renderer::{RenderDevice, RenderQueue},3texture::{DefaultImageSampler, GpuImage},4};5use bevy_asset::RenderAssetUsages;6use bevy_derive::{Deref, DerefMut};7use bevy_ecs::{8prelude::{FromWorld, Res, ResMut},9resource::Resource,10system::SystemParam,11};12use bevy_image::{BevyDefault, Image, ImageSampler, TextureFormatPixelInfo};13use bevy_platform::collections::HashMap;1415/// A [`RenderApp`](crate::RenderApp) resource that contains the default "fallback image",16/// which can be used in situations where an image was not explicitly defined. The most common17/// use case is [`AsBindGroup`] implementations (such as materials) that support optional textures.18///19/// Defaults to a 1x1 fully opaque white texture, (1.0, 1.0, 1.0, 1.0) which makes multiplying20/// it with other colors a no-op.21#[derive(Resource)]22pub struct FallbackImage {23/// Fallback image for [`TextureViewDimension::D1`].24pub d1: GpuImage,25/// Fallback image for [`TextureViewDimension::D2`].26pub d2: GpuImage,27/// Fallback image for [`TextureViewDimension::D2Array`].28pub d2_array: GpuImage,29/// Fallback image for [`TextureViewDimension::Cube`].30pub cube: GpuImage,31/// Fallback image for [`TextureViewDimension::CubeArray`].32pub cube_array: GpuImage,33/// Fallback image for [`TextureViewDimension::D3`].34pub d3: GpuImage,35}3637impl FallbackImage {38/// Returns the appropriate fallback image for the given texture dimension.39pub fn get(&self, texture_dimension: TextureViewDimension) -> &GpuImage {40match texture_dimension {41TextureViewDimension::D1 => &self.d1,42TextureViewDimension::D2 => &self.d2,43TextureViewDimension::D2Array => &self.d2_array,44TextureViewDimension::Cube => &self.cube,45TextureViewDimension::CubeArray => &self.cube_array,46TextureViewDimension::D3 => &self.d3,47}48}49}5051/// A [`RenderApp`](crate::RenderApp) resource that contains a _zero-filled_ "fallback image",52/// which can be used in place of [`FallbackImage`], when a fully transparent or black fallback53/// is required instead of fully opaque white.54///55/// Defaults to a 1x1 fully transparent black texture, (0.0, 0.0, 0.0, 0.0) which makes adding56/// or alpha-blending it to other colors a no-op.57#[derive(Resource, Deref)]58pub struct FallbackImageZero(GpuImage);5960/// A [`RenderApp`](crate::RenderApp) resource that contains a "cubemap fallback image",61/// which can be used in situations where an image was not explicitly defined. The most common62/// use case is [`AsBindGroup`] implementations (such as materials) that support optional textures.63#[derive(Resource, Deref)]64pub struct FallbackImageCubemap(GpuImage);6566fn fallback_image_new(67render_device: &RenderDevice,68render_queue: &RenderQueue,69default_sampler: &DefaultImageSampler,70format: TextureFormat,71dimension: TextureViewDimension,72samples: u32,73value: u8,74) -> GpuImage {75// TODO make this configurable per channel7677let extents = Extent3d {78width: 1,79height: 1,80depth_or_array_layers: match dimension {81TextureViewDimension::Cube | TextureViewDimension::CubeArray => 6,82_ => 1,83},84};8586// We can't create textures with data when it's a depth texture or when using multiple samples87let create_texture_with_data = !format.is_depth_stencil_format() && samples == 1;8889let image_dimension = dimension.compatible_texture_dimension();90let mut image = if create_texture_with_data {91let data = vec![value; format.pixel_size().unwrap_or(0)];92Image::new_fill(93extents,94image_dimension,95&data,96format,97RenderAssetUsages::RENDER_WORLD,98)99} else {100let mut image = Image::default_uninit();101image.texture_descriptor.dimension = TextureDimension::D2;102image.texture_descriptor.size = extents;103image.texture_descriptor.format = format;104image105};106image.texture_descriptor.sample_count = samples;107if image_dimension == TextureDimension::D2 {108image.texture_descriptor.usage |= TextureUsages::RENDER_ATTACHMENT;109}110111let texture = if create_texture_with_data {112render_device.create_texture_with_data(113render_queue,114&image.texture_descriptor,115TextureDataOrder::default(),116&image.data.expect("Image has no data"),117)118} else {119render_device.create_texture(&image.texture_descriptor)120};121122let texture_view = texture.create_view(&TextureViewDescriptor {123dimension: Some(dimension),124array_layer_count: Some(extents.depth_or_array_layers),125..TextureViewDescriptor::default()126});127let sampler = match image.sampler {128ImageSampler::Default => (**default_sampler).clone(),129ImageSampler::Descriptor(ref descriptor) => {130render_device.create_sampler(&descriptor.as_wgpu())131}132};133GpuImage {134texture,135texture_view,136texture_format: image.texture_descriptor.format,137sampler,138size: image.texture_descriptor.size,139mip_level_count: image.texture_descriptor.mip_level_count,140}141}142143impl FromWorld for FallbackImage {144fn from_world(world: &mut bevy_ecs::prelude::World) -> Self {145let render_device = world.resource::<RenderDevice>();146let render_queue = world.resource::<RenderQueue>();147let default_sampler = world.resource::<DefaultImageSampler>();148Self {149d1: fallback_image_new(150render_device,151render_queue,152default_sampler,153TextureFormat::bevy_default(),154TextureViewDimension::D1,1551,156255,157),158d2: fallback_image_new(159render_device,160render_queue,161default_sampler,162TextureFormat::bevy_default(),163TextureViewDimension::D2,1641,165255,166),167d2_array: fallback_image_new(168render_device,169render_queue,170default_sampler,171TextureFormat::bevy_default(),172TextureViewDimension::D2Array,1731,174255,175),176cube: fallback_image_new(177render_device,178render_queue,179default_sampler,180TextureFormat::bevy_default(),181TextureViewDimension::Cube,1821,183255,184),185cube_array: fallback_image_new(186render_device,187render_queue,188default_sampler,189TextureFormat::bevy_default(),190TextureViewDimension::CubeArray,1911,192255,193),194d3: fallback_image_new(195render_device,196render_queue,197default_sampler,198TextureFormat::bevy_default(),199TextureViewDimension::D3,2001,201255,202),203}204}205}206207impl FromWorld for FallbackImageZero {208fn from_world(world: &mut bevy_ecs::prelude::World) -> Self {209let render_device = world.resource::<RenderDevice>();210let render_queue = world.resource::<RenderQueue>();211let default_sampler = world.resource::<DefaultImageSampler>();212Self(fallback_image_new(213render_device,214render_queue,215default_sampler,216TextureFormat::bevy_default(),217TextureViewDimension::D2,2181,2190,220))221}222}223224impl FromWorld for FallbackImageCubemap {225fn from_world(world: &mut bevy_ecs::prelude::World) -> Self {226let render_device = world.resource::<RenderDevice>();227let render_queue = world.resource::<RenderQueue>();228let default_sampler = world.resource::<DefaultImageSampler>();229Self(fallback_image_new(230render_device,231render_queue,232default_sampler,233TextureFormat::bevy_default(),234TextureViewDimension::Cube,2351,236255,237))238}239}240241/// A Cache of fallback textures that uses the sample count and `TextureFormat` as a key242///243/// # WARNING244/// Images using MSAA with sample count > 1 are not initialized with data, therefore,245/// you shouldn't sample them before writing data to them first.246#[derive(Resource, Deref, DerefMut, Default)]247pub struct FallbackImageFormatMsaaCache(HashMap<(u32, TextureFormat), GpuImage>);248249#[derive(SystemParam)]250pub struct FallbackImageMsaa<'w> {251cache: ResMut<'w, FallbackImageFormatMsaaCache>,252render_device: Res<'w, RenderDevice>,253render_queue: Res<'w, RenderQueue>,254default_sampler: Res<'w, DefaultImageSampler>,255}256257impl<'w> FallbackImageMsaa<'w> {258pub fn image_for_samplecount(&mut self, sample_count: u32, format: TextureFormat) -> &GpuImage {259self.cache.entry((sample_count, format)).or_insert_with(|| {260fallback_image_new(261&self.render_device,262&self.render_queue,263&self.default_sampler,264format,265TextureViewDimension::D2,266sample_count,267255,268)269})270}271}272273274