1use crate::{
2 render_asset::RenderAssetUsages,
3 render_resource::*,
4 renderer::{RenderDevice, RenderQueue},
5 texture::{DefaultImageSampler, GpuImage},
6};
7use bevy_derive::{Deref, DerefMut};
8use bevy_ecs::{
9 prelude::{FromWorld, Res, ResMut},
10 system::{Resource, SystemParam},
11};
12use bevy_image::{BevyDefault, Image, ImageSampler, TextureFormatPixelInfo};
13use bevy_utils::HashMap;
14
15#[derive(Resource)]
22pub struct FallbackImage {
23 pub d1: GpuImage,
25 pub d2: GpuImage,
27 pub d2_array: GpuImage,
29 pub cube: GpuImage,
31 pub cube_array: GpuImage,
33 pub d3: GpuImage,
35}
36
37#[derive(Resource, Deref)]
44pub struct FallbackImageZero(GpuImage);
45
46#[derive(Resource, Deref)]
50pub struct FallbackImageCubemap(GpuImage);
51
52fn fallback_image_new(
53 render_device: &RenderDevice,
54 render_queue: &RenderQueue,
55 default_sampler: &DefaultImageSampler,
56 format: TextureFormat,
57 dimension: TextureViewDimension,
58 samples: u32,
59 value: u8,
60) -> GpuImage {
61 let extents = Extent3d {
64 width: 1,
65 height: 1,
66 depth_or_array_layers: match dimension {
67 TextureViewDimension::Cube | TextureViewDimension::CubeArray => 6,
68 _ => 1,
69 },
70 };
71
72 let create_texture_with_data = !format.is_depth_stencil_format() && samples == 1;
74
75 let image_dimension = dimension.compatible_texture_dimension();
76 let mut image = if create_texture_with_data {
77 let data = vec![value; format.pixel_size()];
78 Image::new_fill(
79 extents,
80 image_dimension,
81 &data,
82 format,
83 RenderAssetUsages::RENDER_WORLD,
84 )
85 } else {
86 let mut image = Image::default();
87 image.texture_descriptor.dimension = TextureDimension::D2;
88 image.texture_descriptor.size = extents;
89 image.texture_descriptor.format = format;
90 image
91 };
92 image.texture_descriptor.sample_count = samples;
93 if image_dimension == TextureDimension::D2 {
94 image.texture_descriptor.usage |= TextureUsages::RENDER_ATTACHMENT;
95 }
96
97 let texture = if create_texture_with_data {
98 render_device.create_texture_with_data(
99 render_queue,
100 &image.texture_descriptor,
101 TextureDataOrder::default(),
102 &image.data,
103 )
104 } else {
105 render_device.create_texture(&image.texture_descriptor)
106 };
107
108 let texture_view = texture.create_view(&TextureViewDescriptor {
109 dimension: Some(dimension),
110 array_layer_count: Some(extents.depth_or_array_layers),
111 ..TextureViewDescriptor::default()
112 });
113 let sampler = match image.sampler {
114 ImageSampler::Default => (**default_sampler).clone(),
115 ImageSampler::Descriptor(ref descriptor) => {
116 render_device.create_sampler(&descriptor.as_wgpu())
117 }
118 };
119 GpuImage {
120 texture,
121 texture_view,
122 texture_format: image.texture_descriptor.format,
123 sampler,
124 size: image.size(),
125 mip_level_count: image.texture_descriptor.mip_level_count,
126 }
127}
128
129impl FromWorld for FallbackImage {
130 fn from_world(world: &mut bevy_ecs::prelude::World) -> Self {
131 let render_device = world.resource::<RenderDevice>();
132 let render_queue = world.resource::<RenderQueue>();
133 let default_sampler = world.resource::<DefaultImageSampler>();
134 Self {
135 d1: fallback_image_new(
136 render_device,
137 render_queue,
138 default_sampler,
139 TextureFormat::bevy_default(),
140 TextureViewDimension::D1,
141 1,
142 255,
143 ),
144 d2: fallback_image_new(
145 render_device,
146 render_queue,
147 default_sampler,
148 TextureFormat::bevy_default(),
149 TextureViewDimension::D2,
150 1,
151 255,
152 ),
153 d2_array: fallback_image_new(
154 render_device,
155 render_queue,
156 default_sampler,
157 TextureFormat::bevy_default(),
158 TextureViewDimension::D2Array,
159 1,
160 255,
161 ),
162 cube: fallback_image_new(
163 render_device,
164 render_queue,
165 default_sampler,
166 TextureFormat::bevy_default(),
167 TextureViewDimension::Cube,
168 1,
169 255,
170 ),
171 cube_array: fallback_image_new(
172 render_device,
173 render_queue,
174 default_sampler,
175 TextureFormat::bevy_default(),
176 TextureViewDimension::CubeArray,
177 1,
178 255,
179 ),
180 d3: fallback_image_new(
181 render_device,
182 render_queue,
183 default_sampler,
184 TextureFormat::bevy_default(),
185 TextureViewDimension::D3,
186 1,
187 255,
188 ),
189 }
190 }
191}
192
193impl FromWorld for FallbackImageZero {
194 fn from_world(world: &mut bevy_ecs::prelude::World) -> Self {
195 let render_device = world.resource::<RenderDevice>();
196 let render_queue = world.resource::<RenderQueue>();
197 let default_sampler = world.resource::<DefaultImageSampler>();
198 Self(fallback_image_new(
199 render_device,
200 render_queue,
201 default_sampler,
202 TextureFormat::bevy_default(),
203 TextureViewDimension::D2,
204 1,
205 0,
206 ))
207 }
208}
209
210impl FromWorld for FallbackImageCubemap {
211 fn from_world(world: &mut bevy_ecs::prelude::World) -> Self {
212 let render_device = world.resource::<RenderDevice>();
213 let render_queue = world.resource::<RenderQueue>();
214 let default_sampler = world.resource::<DefaultImageSampler>();
215 Self(fallback_image_new(
216 render_device,
217 render_queue,
218 default_sampler,
219 TextureFormat::bevy_default(),
220 TextureViewDimension::Cube,
221 1,
222 255,
223 ))
224 }
225}
226
227#[derive(Resource, Deref, DerefMut, Default)]
233pub struct FallbackImageFormatMsaaCache(HashMap<(u32, TextureFormat), GpuImage>);
234
235#[derive(SystemParam)]
236pub struct FallbackImageMsaa<'w> {
237 cache: ResMut<'w, FallbackImageFormatMsaaCache>,
238 render_device: Res<'w, RenderDevice>,
239 render_queue: Res<'w, RenderQueue>,
240 default_sampler: Res<'w, DefaultImageSampler>,
241}
242
243impl<'w> FallbackImageMsaa<'w> {
244 pub fn image_for_samplecount(&mut self, sample_count: u32, format: TextureFormat) -> &GpuImage {
245 self.cache.entry((sample_count, format)).or_insert_with(|| {
246 fallback_image_new(
247 &self.render_device,
248 &self.render_queue,
249 &self.default_sampler,
250 format,
251 TextureViewDimension::D2,
252 sample_count,
253 255,
254 )
255 })
256 }
257}