Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_render/src/texture/gpu_image.rs
9343 views
1
use crate::{
2
render_asset::{AssetExtractionError, PrepareAssetError, RenderAsset},
3
render_resource::{DefaultImageSampler, Sampler, Texture, TextureView},
4
renderer::{RenderDevice, RenderQueue},
5
};
6
use bevy_asset::{AssetId, RenderAssetUsages};
7
use bevy_ecs::system::{lifetimeless::SRes, SystemParamItem};
8
use bevy_image::{Image, ImageSampler};
9
use bevy_log::warn;
10
use bevy_math::{AspectRatio, UVec2};
11
use wgpu::{Extent3d, TexelCopyBufferLayout, TextureFormat, TextureUsages};
12
use wgpu_types::{TextureDescriptor, TextureViewDescriptor};
13
14
/// The GPU-representation of an [`Image`].
15
/// Consists of the [`Texture`], its [`TextureView`] and the corresponding [`Sampler`], and the texture's size.
16
#[derive(Debug, Clone)]
17
pub struct GpuImage {
18
pub texture: Texture,
19
pub texture_view: TextureView,
20
pub sampler: Sampler,
21
pub texture_descriptor: TextureDescriptor<Option<&'static str>, &'static [TextureFormat]>,
22
pub texture_view_descriptor: Option<TextureViewDescriptor<Option<&'static str>>>,
23
pub had_data: bool,
24
}
25
26
impl RenderAsset for GpuImage {
27
type SourceAsset = Image;
28
type Param = (
29
SRes<RenderDevice>,
30
SRes<RenderQueue>,
31
SRes<DefaultImageSampler>,
32
);
33
34
#[inline]
35
fn asset_usage(image: &Self::SourceAsset) -> RenderAssetUsages {
36
image.asset_usage
37
}
38
39
fn take_gpu_data(
40
source: &mut Self::SourceAsset,
41
previous_gpu_asset: Option<&Self>,
42
) -> Result<Self::SourceAsset, AssetExtractionError> {
43
let data = source.data.take();
44
45
// check if this image originally had data and no longer does, that implies it
46
// has already been extracted
47
let valid_upload = data.is_some() || previous_gpu_asset.is_none_or(|prev| !prev.had_data);
48
49
valid_upload
50
.then(|| Self::SourceAsset {
51
data,
52
..source.clone()
53
})
54
.ok_or(AssetExtractionError::AlreadyExtracted)
55
}
56
57
#[inline]
58
fn byte_len(image: &Self::SourceAsset) -> Option<usize> {
59
image.data.as_ref().map(Vec::len)
60
}
61
62
/// Converts the extracted image into a [`GpuImage`].
63
fn prepare_asset(
64
image: Self::SourceAsset,
65
_: AssetId<Self::SourceAsset>,
66
(render_device, render_queue, default_sampler): &mut SystemParamItem<Self::Param>,
67
previous_asset: Option<&Self>,
68
) -> Result<Self, PrepareAssetError<Self::SourceAsset>> {
69
let had_data = image.data.is_some();
70
let texture = if let Some(prev) = previous_asset
71
&& prev.texture_descriptor == image.texture_descriptor
72
&& prev
73
.texture_descriptor
74
.usage
75
.contains(TextureUsages::COPY_DST)
76
&& let Some(block_bytes) = image.texture_descriptor.format.block_copy_size(None)
77
{
78
if let Some(ref data) = image.data {
79
let (block_width, block_height) =
80
image.texture_descriptor.format.block_dimensions();
81
82
// queue copy
83
render_queue.write_texture(
84
prev.texture.as_image_copy(),
85
data,
86
TexelCopyBufferLayout {
87
offset: 0,
88
bytes_per_row: Some(image.width() / block_width * block_bytes),
89
rows_per_image: Some(image.height() / block_height),
90
},
91
image.texture_descriptor.size,
92
);
93
}
94
95
if !image.copy_on_resize {
96
// TODO else could clear here? probably not necessary as textures without data are only useful as render
97
// targets and will normally be overwritten immediately anyway
98
}
99
100
// reuse previous texture
101
prev.texture.clone()
102
} else if let Some(ref data) = image.data {
103
render_device.create_texture_with_data(
104
render_queue,
105
&image.texture_descriptor,
106
image.data_order,
107
data,
108
)
109
} else {
110
let new_texture = render_device.create_texture(&image.texture_descriptor);
111
if image.copy_on_resize {
112
if let Some(previous) = previous_asset {
113
let mut command_encoder =
114
render_device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
115
label: Some("copy_image_on_resize"),
116
});
117
let copy_size = Extent3d {
118
width: image
119
.texture_descriptor
120
.size
121
.width
122
.min(previous.texture_descriptor.size.width),
123
height: image
124
.texture_descriptor
125
.size
126
.height
127
.min(previous.texture_descriptor.size.height),
128
depth_or_array_layers: image
129
.texture_descriptor
130
.size
131
.depth_or_array_layers
132
.min(previous.texture_descriptor.size.depth_or_array_layers),
133
};
134
135
command_encoder.copy_texture_to_texture(
136
previous.texture.as_image_copy(),
137
new_texture.as_image_copy(),
138
copy_size,
139
);
140
render_queue.submit([command_encoder.finish()]);
141
} else {
142
warn!("No previous asset to copy from for image: {:?}", image);
143
}
144
}
145
new_texture
146
};
147
148
let texture_view = if let Some(prev) = previous_asset.as_ref()
149
&& prev.texture_descriptor == image.texture_descriptor
150
&& prev
151
.texture_descriptor
152
.usage
153
.contains(TextureUsages::COPY_DST)
154
&& prev.texture_view_descriptor == image.texture_view_descriptor
155
{
156
prev.texture_view.clone()
157
} else {
158
image
159
.texture_view_descriptor
160
.as_ref()
161
.map(|desc| texture.create_view(desc))
162
.unwrap_or_else(|| texture.create_view(&TextureViewDescriptor::default()))
163
};
164
let sampler = match image.sampler {
165
ImageSampler::Default => (***default_sampler).clone(),
166
ImageSampler::Descriptor(descriptor) => {
167
render_device.create_sampler(&descriptor.as_wgpu())
168
}
169
};
170
171
Ok(GpuImage {
172
texture,
173
texture_view,
174
sampler,
175
texture_descriptor: image.texture_descriptor,
176
texture_view_descriptor: image.texture_view_descriptor,
177
had_data,
178
})
179
}
180
}
181
182
impl GpuImage {
183
/// Returns the aspect ratio (width / height) of a 2D image.
184
#[inline]
185
pub fn aspect_ratio(&self) -> AspectRatio {
186
AspectRatio::try_from_pixels(
187
self.texture_descriptor.size.width,
188
self.texture_descriptor.size.height,
189
)
190
.expect(
191
"Failed to calculate aspect ratio: Image dimensions must be positive, non-zero values",
192
)
193
}
194
195
/// Returns the size of a 2D image.
196
#[inline]
197
pub fn size_2d(&self) -> UVec2 {
198
UVec2::new(
199
self.texture_descriptor.size.width,
200
self.texture_descriptor.size.height,
201
)
202
}
203
204
/// Gets the view format of this image.
205
/// If the view format is not explicitly provided, falls back to the base image format
206
#[inline]
207
pub fn view_format(&self) -> TextureFormat {
208
self.texture_view_descriptor
209
.as_ref()
210
.and_then(|view_desc| view_desc.format)
211
.unwrap_or(self.texture_descriptor.format)
212
}
213
}
214
215