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