1use crate::{
2 render_resource::*,
3 renderer::{RenderDevice, RenderQueue},
4 texture::{DefaultImageSampler, GpuImage},
5};
6use bevy_asset::RenderAssetUsages;
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().unwrap_or(0)];
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 texture_view_format: image.texture_view_descriptor.and_then(|v| v.format),
139 sampler,
140 size: image.texture_descriptor.size,
141 mip_level_count: image.texture_descriptor.mip_level_count,
142 had_data: true,
143 }
144}
145
146impl FromWorld for FallbackImage {
147 fn from_world(world: &mut bevy_ecs::prelude::World) -> Self {
148 let render_device = world.resource::<RenderDevice>();
149 let render_queue = world.resource::<RenderQueue>();
150 let default_sampler = world.resource::<DefaultImageSampler>();
151 Self {
152 d1: fallback_image_new(
153 render_device,
154 render_queue,
155 default_sampler,
156 TextureFormat::bevy_default(),
157 TextureViewDimension::D1,
158 1,
159 255,
160 ),
161 d2: fallback_image_new(
162 render_device,
163 render_queue,
164 default_sampler,
165 TextureFormat::bevy_default(),
166 TextureViewDimension::D2,
167 1,
168 255,
169 ),
170 d2_array: fallback_image_new(
171 render_device,
172 render_queue,
173 default_sampler,
174 TextureFormat::bevy_default(),
175 TextureViewDimension::D2Array,
176 1,
177 255,
178 ),
179 cube: fallback_image_new(
180 render_device,
181 render_queue,
182 default_sampler,
183 TextureFormat::bevy_default(),
184 TextureViewDimension::Cube,
185 1,
186 255,
187 ),
188 cube_array: fallback_image_new(
189 render_device,
190 render_queue,
191 default_sampler,
192 TextureFormat::bevy_default(),
193 TextureViewDimension::CubeArray,
194 1,
195 255,
196 ),
197 d3: fallback_image_new(
198 render_device,
199 render_queue,
200 default_sampler,
201 TextureFormat::bevy_default(),
202 TextureViewDimension::D3,
203 1,
204 255,
205 ),
206 }
207 }
208}
209
210impl FromWorld for FallbackImageZero {
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::D2,
221 1,
222 0,
223 ))
224 }
225}
226
227impl FromWorld for FallbackImageCubemap {
228 fn from_world(world: &mut bevy_ecs::prelude::World) -> Self {
229 let render_device = world.resource::<RenderDevice>();
230 let render_queue = world.resource::<RenderQueue>();
231 let default_sampler = world.resource::<DefaultImageSampler>();
232 Self(fallback_image_new(
233 render_device,
234 render_queue,
235 default_sampler,
236 TextureFormat::bevy_default(),
237 TextureViewDimension::Cube,
238 1,
239 255,
240 ))
241 }
242}
243
244#[derive(Resource, Deref, DerefMut, Default)]
250pub struct FallbackImageFormatMsaaCache(HashMap<(u32, TextureFormat), GpuImage>);
251
252#[derive(SystemParam)]
253pub struct FallbackImageMsaa<'w> {
254 cache: ResMut<'w, FallbackImageFormatMsaaCache>,
255 render_device: Res<'w, RenderDevice>,
256 render_queue: Res<'w, RenderQueue>,
257 default_sampler: Res<'w, DefaultImageSampler>,
258}
259
260impl<'w> FallbackImageMsaa<'w> {
261 pub fn image_for_samplecount(&mut self, sample_count: u32, format: TextureFormat) -> &GpuImage {
262 self.cache.entry((sample_count, format)).or_insert_with(|| {
263 fallback_image_new(
264 &self.render_device,
265 &self.render_queue,
266 &self.default_sampler,
267 format,
268 TextureViewDimension::D2,
269 sample_count,
270 255,
271 )
272 })
273 }
274}