Skip to content

Commit 27fd2c4

Browse files
authored
Reuse gpu textures when possible (#22552)
# Objective when an image's data is modified, we currently always create a new gpu texture. if the size and format haven't changed, we could instead reuse the existing texture. this is more efficient for cpu image data modifications, and also means we don't need to `get_mut` materials using these textures to propagate the changes, unless the descriptor has changed. ## Solution - put the full texture descriptor and view descriptor into the GpuImage so we can compare fully - reuse the existing texture and view when the descriptors are unchanged
1 parent 67dfeb9 commit 27fd2c4

File tree

13 files changed

+140
-72
lines changed

13 files changed

+140
-72
lines changed

crates/bevy_core_pipeline/src/mip_generation/mod.rs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,7 @@ impl Node for MipGenerationNode {
426426
};
427427
let Some(mip_generation_pipelines) = mip_generation_bind_groups
428428
.pipelines
429-
.get(&gpu_image.texture_format)
429+
.get(&gpu_image.texture_descriptor.format)
430430
else {
431431
continue;
432432
};
@@ -458,8 +458,8 @@ impl Node for MipGenerationNode {
458458
&[],
459459
);
460460
compute_pass_1.dispatch_workgroups(
461-
gpu_image.size.width.div_ceil(64),
462-
gpu_image.size.height.div_ceil(64),
461+
gpu_image.texture_descriptor.size.width.div_ceil(64),
462+
gpu_image.texture_descriptor.size.height.div_ceil(64),
463463
1,
464464
);
465465
pass_span.end(&mut compute_pass_1);
@@ -482,8 +482,8 @@ impl Node for MipGenerationNode {
482482
&[],
483483
);
484484
compute_pass_2.dispatch_workgroups(
485-
gpu_image.size.width.div_ceil(256),
486-
gpu_image.size.height.div_ceil(256),
485+
gpu_image.texture_descriptor.size.width.div_ceil(256),
486+
gpu_image.texture_descriptor.size.height.div_ceil(256),
487487
1,
488488
);
489489
pass_span.end(&mut compute_pass_2);
@@ -538,7 +538,7 @@ fn prepare_mip_generator_pipelines(
538538
&pipeline_cache,
539539
&downsample_shaders,
540540
&mut mip_generation_pipelines.pipelines,
541-
gpu_image.texture_format,
541+
gpu_image.texture_descriptor.format,
542542
mip_generation_job,
543543
combine_downsampling_bind_groups,
544544
) else {
@@ -786,7 +786,7 @@ fn create_downsampling_bind_groups(
786786
label: Some("mip generation input texture view, pass 2"),
787787
format: Some(gpu_image.texture.format()),
788788
dimension: Some(TextureViewDimension::D2),
789-
base_mip_level: gpu_image.mip_level_count.min(6),
789+
base_mip_level: gpu_image.texture_descriptor.mip_level_count.min(6),
790790
mip_level_count: Some(1),
791791
..default()
792792
});
@@ -892,10 +892,10 @@ fn create_downsampling_constants_buffer(
892892
gpu_image: &GpuImage,
893893
) -> UniformBuffer<DownsamplingConstants> {
894894
let downsampling_constants = DownsamplingConstants {
895-
mips: gpu_image.mip_level_count,
895+
mips: gpu_image.texture_descriptor.mip_level_count,
896896
inverse_input_size: vec2(
897-
1.0 / gpu_image.size.width as f32,
898-
1.0 / gpu_image.size.height as f32,
897+
1.0 / gpu_image.texture_descriptor.size.width as f32,
898+
1.0 / gpu_image.texture_descriptor.size.height as f32,
899899
),
900900
_padding: 0,
901901
};
@@ -914,13 +914,13 @@ fn get_mip_storage_view(
914914
) -> TextureView {
915915
// If `level` represents an actual mip level of the image, return a view to
916916
// it.
917-
if level < gpu_image.mip_level_count {
917+
if level < gpu_image.texture_descriptor.mip_level_count {
918918
return gpu_image.texture.create_view(&TextureViewDescriptor {
919919
label: Some(&*format!(
920920
"mip downsampling storage view {}/{}",
921-
level, gpu_image.mip_level_count
921+
level, gpu_image.texture_descriptor.mip_level_count
922922
)),
923-
format: Some(gpu_image.texture_format),
923+
format: Some(gpu_image.texture_descriptor.format),
924924
dimension: Some(TextureViewDimension::D2),
925925
aspect: TextureAspect::All,
926926
base_mip_level: level,
@@ -936,7 +936,7 @@ fn get_mip_storage_view(
936936
let dummy_texture = render_device.create_texture(&TextureDescriptor {
937937
label: Some(&*format!(
938938
"mip downsampling dummy storage view {}/{}",
939-
level, gpu_image.mip_level_count
939+
level, gpu_image.texture_descriptor.mip_level_count
940940
)),
941941
size: Extent3d {
942942
width: 1,
@@ -946,7 +946,7 @@ fn get_mip_storage_view(
946946
mip_level_count: 1,
947947
sample_count: 1,
948948
dimension: TextureDimension::D2,
949-
format: gpu_image.texture_format,
949+
format: gpu_image.texture_descriptor.format,
950950
usage: TextureUsages::STORAGE_BINDING | TextureUsages::TEXTURE_BINDING,
951951
view_formats: &[],
952952
});

crates/bevy_pbr/src/light_probe/environment_map.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ impl LightProbeComponent for EnvironmentMapLight {
288288
diffuse: diffuse_map_handle.id(),
289289
specular: specular_map_handle.id(),
290290
}) as i32,
291-
smallest_specular_mip_level: specular_map.mip_level_count - 1,
291+
smallest_specular_mip_level: specular_map.texture_descriptor.mip_level_count - 1,
292292
intensity: *intensity,
293293
affects_lightmapped_mesh_diffuse: *affects_lightmapped_mesh_diffuse,
294294
};

crates/bevy_pbr/src/light_probe/generate.rs

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -526,7 +526,7 @@ pub fn prepare_generated_environment_map_intermediate_textures(
526526
mut commands: Commands,
527527
) {
528528
for (entity, env_map_light) in &light_probes {
529-
let base_size = env_map_light.environment_map.size.width;
529+
let base_size = env_map_light.environment_map.texture_descriptor.size.width;
530530
let mip_level_count = compute_mip_count(base_size);
531531

532532
let environment_map = texture_cache.get(
@@ -597,16 +597,20 @@ pub fn prepare_generated_environment_map_bind_groups(
597597
return;
598598
};
599599

600-
assert!(stbn_texture.size.width.is_power_of_two());
601-
assert!(stbn_texture.size.height.is_power_of_two());
600+
assert!(stbn_texture.texture_descriptor.size.width.is_power_of_two());
601+
assert!(stbn_texture
602+
.texture_descriptor
603+
.size
604+
.height
605+
.is_power_of_two());
602606
let noise_size_bits = UVec2::new(
603-
stbn_texture.size.width.trailing_zeros(),
604-
stbn_texture.size.height.trailing_zeros(),
607+
stbn_texture.texture_descriptor.size.width.trailing_zeros(),
608+
stbn_texture.texture_descriptor.size.height.trailing_zeros(),
605609
);
606610

607611
for (entity, textures, env_map_light) in &light_probes {
608612
// Determine mip chain based on input size
609-
let base_size = env_map_light.environment_map.size.width;
613+
let base_size = env_map_light.environment_map.texture_descriptor.size.width;
610614
let mip_count = compute_mip_count(base_size);
611615
let last_mip = mip_count - 1;
612616
let env_map_texture = env_map_light.environment_map.texture.clone();
@@ -923,7 +927,7 @@ impl Node for DownsamplingNode {
923927
compute_pass.set_pipeline(copy_pipeline);
924928
compute_pass.set_bind_group(0, &bind_groups.copy, &[]);
925929

926-
let tex_size = env_map_light.environment_map.size;
930+
let tex_size = env_map_light.environment_map.texture_descriptor.size;
927931
let wg_x = tex_size.width.div_ceil(8);
928932
let wg_y = tex_size.height.div_ceil(8);
929933
compute_pass.dispatch_workgroups(wg_x, wg_y, 6);
@@ -947,7 +951,7 @@ impl Node for DownsamplingNode {
947951
compute_pass.set_pipeline(downsample_first_pipeline);
948952
compute_pass.set_bind_group(0, &bind_groups.downsampling_first, &[]);
949953

950-
let tex_size = env_map_light.environment_map.size;
954+
let tex_size = env_map_light.environment_map.texture_descriptor.size;
951955
let wg_x = tex_size.width.div_ceil(64);
952956
let wg_y = tex_size.height.div_ceil(64);
953957
compute_pass.dispatch_workgroups(wg_x, wg_y, 6); // 6 faces
@@ -971,7 +975,7 @@ impl Node for DownsamplingNode {
971975
compute_pass.set_pipeline(downsample_second_pipeline);
972976
compute_pass.set_bind_group(0, &bind_groups.downsampling_second, &[]);
973977

974-
let tex_size = env_map_light.environment_map.size;
978+
let tex_size = env_map_light.environment_map.texture_descriptor.size;
975979
let wg_x = tex_size.width.div_ceil(256);
976980
let wg_y = tex_size.height.div_ceil(256);
977981
compute_pass.dispatch_workgroups(wg_x, wg_y, 6);
@@ -1039,7 +1043,7 @@ impl Node for FilteringNode {
10391043

10401044
compute_pass.set_pipeline(radiance_pipeline);
10411045

1042-
let base_size = env_map_light.specular_map.size.width;
1046+
let base_size = env_map_light.specular_map.texture_descriptor.size.width;
10431047

10441048
// Radiance convolution pass
10451049
// Process each mip at different roughness levels

crates/bevy_pbr/src/pbr_material.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1145,7 +1145,7 @@ impl AsBindGroupShaderType<StandardMaterialUniform> for StandardMaterial {
11451145
if has_normal_map {
11461146
let normal_map_id = self.normal_map_texture.as_ref().map(Handle::id).unwrap();
11471147
if let Some(texture) = images.get(normal_map_id) {
1148-
match texture.texture_format {
1148+
match texture.texture_descriptor.format {
11491149
// All 2-component unorm formats
11501150
TextureFormat::Rg8Unorm
11511151
| TextureFormat::Rg16Unorm

crates/bevy_pbr/src/render/mesh.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2065,11 +2065,9 @@ pub fn build_dummy_white_gpu_image(
20652065
GpuImage {
20662066
texture,
20672067
texture_view,
2068-
texture_format: image.texture_descriptor.format,
2069-
texture_view_format: image.texture_view_descriptor.and_then(|v| v.format),
20702068
sampler,
2071-
size: image.texture_descriptor.size,
2072-
mip_level_count: image.texture_descriptor.mip_level_count,
2069+
texture_descriptor: image.texture_descriptor,
2070+
texture_view_descriptor: image.texture_view_descriptor,
20732071
had_data: true,
20742072
}
20752073
}

crates/bevy_render/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ wgpu = { version = "27", default-features = false, features = [
9595
"naga-ir",
9696
"fragile-send-sync-non-atomic-wasm",
9797
] }
98+
wgpu-types = { version = "27", default-features = false }
9899
naga = { version = "27", features = ["wgsl-in"] }
99100
bytemuck = { version = "1.5", features = ["derive", "must_cast"] }
100101
downcast-rs = { version = "2", default-features = false, features = ["std"] }

crates/bevy_render/macros/src/as_bind_group.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -722,7 +722,7 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result<TokenStream> {
722722
if let Some(handle) = handle {
723723
let image = images.get(handle).ok_or_else(|| #render_path::render_resource::AsBindGroupError::RetryNextUpdate)?;
724724

725-
let Some(sample_type) = image.texture_format.sample_type(None, Some(render_device.features())) else {
725+
let Some(sample_type) = image.texture_descriptor.format.sample_type(None, Some(render_device.features())) else {
726726
return Err(#render_path::render_resource::AsBindGroupError::InvalidSamplerType(
727727
#binding_index,
728728
"None".to_string(),

crates/bevy_render/src/camera.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -210,9 +210,9 @@ impl NormalizedRenderTargetExt for NormalizedRenderTarget {
210210
NormalizedRenderTarget::Window(window_ref) => windows
211211
.get(&window_ref.entity())
212212
.and_then(|window| window.swap_chain_texture_view_format),
213-
NormalizedRenderTarget::Image(image_target) => images
214-
.get(&image_target.handle)
215-
.map(|image| image.texture_view_format.unwrap_or(image.texture_format)),
213+
NormalizedRenderTarget::Image(image_target) => {
214+
images.get(&image_target.handle).map(GpuImage::view_format)
215+
}
216216
NormalizedRenderTarget::TextureView(id) => {
217217
manual_texture_views.get(id).map(|tex| tex.view_format)
218218
}

crates/bevy_render/src/gpu_readback.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -262,20 +262,24 @@ fn prepare_buffers(
262262
match readback {
263263
Readback::Texture(image) => {
264264
if let Some(gpu_image) = gpu_images.get(image)
265-
&& let Ok(pixel_size) = gpu_image.texture_format.pixel_size()
265+
&& let Ok(pixel_size) = gpu_image.texture_descriptor.format.pixel_size()
266266
{
267-
let layout = layout_data(gpu_image.size, gpu_image.texture_format);
267+
let layout = layout_data(
268+
gpu_image.texture_descriptor.size,
269+
gpu_image.texture_descriptor.format,
270+
);
268271
let buffer = buffer_pool.get(
269272
&render_device,
270-
get_aligned_size(gpu_image.size, pixel_size as u32) as u64,
273+
get_aligned_size(gpu_image.texture_descriptor.size, pixel_size as u32)
274+
as u64,
271275
);
272276
let (tx, rx) = async_channel::bounded(1);
273277
readbacks.requested.push(GpuReadback {
274278
entity: entity.id(),
275279
src: ReadbackSource::Texture {
276280
texture: gpu_image.texture.clone(),
277281
layout,
278-
size: gpu_image.size,
282+
size: gpu_image.texture_descriptor.size,
279283
},
280284
buffer,
281285
rx,

crates/bevy_render/src/texture/fallback_image.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -134,11 +134,9 @@ fn fallback_image_new(
134134
GpuImage {
135135
texture,
136136
texture_view,
137-
texture_format: image.texture_descriptor.format,
138-
texture_view_format: image.texture_view_descriptor.and_then(|v| v.format),
139137
sampler,
140-
size: image.texture_descriptor.size,
141-
mip_level_count: image.texture_descriptor.mip_level_count,
138+
texture_descriptor: image.texture_descriptor,
139+
texture_view_descriptor: image.texture_view_descriptor,
142140
had_data: true,
143141
}
144142
}

0 commit comments

Comments
 (0)