Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_render/src/texture/fallback_image.rs
6596 views
1
use crate::{
2
render_resource::*,
3
renderer::{RenderDevice, RenderQueue},
4
texture::{DefaultImageSampler, GpuImage},
5
};
6
use bevy_asset::RenderAssetUsages;
7
use bevy_derive::{Deref, DerefMut};
8
use bevy_ecs::{
9
prelude::{FromWorld, Res, ResMut},
10
resource::Resource,
11
system::SystemParam,
12
};
13
use bevy_image::{BevyDefault, Image, ImageSampler, TextureFormatPixelInfo};
14
use bevy_platform::collections::HashMap;
15
16
/// A [`RenderApp`](crate::RenderApp) resource that contains the default "fallback image",
17
/// which can be used in situations where an image was not explicitly defined. The most common
18
/// use case is [`AsBindGroup`] implementations (such as materials) that support optional textures.
19
///
20
/// Defaults to a 1x1 fully opaque white texture, (1.0, 1.0, 1.0, 1.0) which makes multiplying
21
/// it with other colors a no-op.
22
#[derive(Resource)]
23
pub struct FallbackImage {
24
/// Fallback image for [`TextureViewDimension::D1`].
25
pub d1: GpuImage,
26
/// Fallback image for [`TextureViewDimension::D2`].
27
pub d2: GpuImage,
28
/// Fallback image for [`TextureViewDimension::D2Array`].
29
pub d2_array: GpuImage,
30
/// Fallback image for [`TextureViewDimension::Cube`].
31
pub cube: GpuImage,
32
/// Fallback image for [`TextureViewDimension::CubeArray`].
33
pub cube_array: GpuImage,
34
/// Fallback image for [`TextureViewDimension::D3`].
35
pub d3: GpuImage,
36
}
37
38
impl FallbackImage {
39
/// Returns the appropriate fallback image for the given texture dimension.
40
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
/// A [`RenderApp`](crate::RenderApp) resource that contains a _zero-filled_ "fallback image",
53
/// which can be used in place of [`FallbackImage`], when a fully transparent or black fallback
54
/// is required instead of fully opaque white.
55
///
56
/// Defaults to a 1x1 fully transparent black texture, (0.0, 0.0, 0.0, 0.0) which makes adding
57
/// or alpha-blending it to other colors a no-op.
58
#[derive(Resource, Deref)]
59
pub struct FallbackImageZero(GpuImage);
60
61
/// A [`RenderApp`](crate::RenderApp) resource that contains a "cubemap fallback image",
62
/// which can be used in situations where an image was not explicitly defined. The most common
63
/// use case is [`AsBindGroup`] implementations (such as materials) that support optional textures.
64
#[derive(Resource, Deref)]
65
pub struct FallbackImageCubemap(GpuImage);
66
67
fn 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
// TODO make this configurable per channel
77
78
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
// We can't create textures with data when it's a depth texture or when using multiple samples
88
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
sampler,
139
size: image.texture_descriptor.size,
140
mip_level_count: image.texture_descriptor.mip_level_count,
141
}
142
}
143
144
impl 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
208
impl 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
225
impl 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
/// A Cache of fallback textures that uses the sample count and `TextureFormat` as a key
243
///
244
/// # WARNING
245
/// Images using MSAA with sample count > 1 are not initialized with data, therefore,
246
/// you shouldn't sample them before writing data to them first.
247
#[derive(Resource, Deref, DerefMut, Default)]
248
pub struct FallbackImageFormatMsaaCache(HashMap<(u32, TextureFormat), GpuImage>);
249
250
#[derive(SystemParam)]
251
pub 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
258
impl<'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
}
273
274