Skip to content

Commit 1b6e3f5

Browse files
tigregalischompaa
andauthored
Add Image constructor specialised for rendering to a texture (#17209)
# Objective Fixes #7358 Redo of #7360 Ergonomics. There's a bunch of enigmatic boilerplate for constructing a texture for rendering to, which could be greatly simplified for the external user-facing API. ## Solution - Take part of the render_to_target example and turn it into a new constructor for `Image`, with minimal changes beyond the `Default` implementation. - Update the render_to_target example to use the new API. Strictly speaking, there are two small differences between the constructor and the example: ~~1. The example sets the `size` when initially constructing the `Image`, then `resize`s, but `resize` sets the `size` anyway so we don't need to do this extra step.~~ ~~2. The example sets `Image.texture_descriptor.format` to `TextureFormat::Bgra8UnormSrgb`, but the default impl sets this to `TextureFormat::Rgba8UnormSrgb` via `wgpu::TextureFormat::bevy_default()`. I don't know what sort of impact this has, but it works on my machine.~~ I've deliberately chosen to only include `width` and `height` as parameters, but maybe it makes sense for some of the other properties to be exposed as parameters. --- ## Changelog ### Added Added `Image::new_target_texture` constructor for simpler creation of render target textures. --- Notes: - This is a re-do of #7360 - there's some relevant discussion on code style there. - The docs for the method want to refer to `bevy_render::camera::Camera` and `bevy_render::camera::RenderTarget::Image`. `bevy_image` used to be part of `bevy_render` and was split out in the past, and `bevy_image` doesn't depend on `bevy_render`. What's the recommendation here? --------- Co-authored-by: Antony <[email protected]>
1 parent e5c82f0 commit 1b6e3f5

File tree

4 files changed

+65
-52
lines changed

4 files changed

+65
-52
lines changed

crates/bevy_image/src/image.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -843,6 +843,58 @@ impl Image {
843843
Image::new(size, dimension, data, format, asset_usage)
844844
}
845845

846+
/// Create a new zero-filled image with a given size, which can be rendered to.
847+
/// Useful for mirrors, UI, or exporting images for example.
848+
/// This is primarily for use as a render target for a [`Camera`].
849+
/// See [`RenderTarget::Image`].
850+
///
851+
/// For Standard Dynamic Range (SDR) images you can use [`TextureFormat::Rgba8UnormSrgb`].
852+
/// For High Dynamic Range (HDR) images you can use [`TextureFormat::Rgba16Float`].
853+
///
854+
/// The default [`TextureUsages`] are
855+
/// [`TEXTURE_BINDING`](TextureUsages::TEXTURE_BINDING),
856+
/// [`COPY_DST`](TextureUsages::COPY_DST),
857+
/// [`RENDER_ATTACHMENT`](TextureUsages::RENDER_ATTACHMENT).
858+
///
859+
/// The default [`RenderAssetUsages`] is [`MAIN_WORLD | RENDER_WORLD`](RenderAssetUsages::default)
860+
/// so that it is accessible from the CPU and GPU.
861+
/// You can customize this by changing the [`asset_usage`](Image::asset_usage) field.
862+
///
863+
/// [`Camera`]: https://docs.rs/bevy/latest/bevy/render/camera/struct.Camera.html
864+
/// [`RenderTarget::Image`]: https://docs.rs/bevy/latest/bevy/render/camera/enum.RenderTarget.html#variant.Image
865+
pub fn new_target_texture(width: u32, height: u32, format: TextureFormat) -> Self {
866+
let size = Extent3d {
867+
width,
868+
height,
869+
..Default::default()
870+
};
871+
// You need to set these texture usage flags in order to use the image as a render target
872+
let usage = TextureUsages::TEXTURE_BINDING
873+
| TextureUsages::COPY_DST
874+
| TextureUsages::RENDER_ATTACHMENT;
875+
// Fill with zeroes
876+
let data = vec![0; format.pixel_size() * size.volume()];
877+
878+
Image {
879+
data: Some(data),
880+
data_order: TextureDataOrder::default(),
881+
texture_descriptor: TextureDescriptor {
882+
size,
883+
format,
884+
dimension: TextureDimension::D2,
885+
label: None,
886+
mip_level_count: 1,
887+
sample_count: 1,
888+
usage,
889+
view_formats: &[],
890+
},
891+
sampler: ImageSampler::Default,
892+
texture_view_descriptor: None,
893+
asset_usage: RenderAssetUsages::default(),
894+
copy_on_resize: true,
895+
}
896+
}
897+
846898
/// Returns the width of a 2D image.
847899
#[inline]
848900
pub fn width(&self) -> u32 {

examples/3d/render_to_texture.rs

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,7 @@ use std::f32::consts::PI;
44

55
use bevy::{
66
prelude::*,
7-
render::{
8-
render_asset::RenderAssetUsages,
9-
render_resource::{Extent3d, TextureDimension, TextureFormat, TextureUsages},
10-
view::RenderLayers,
11-
},
7+
render::{render_resource::TextureFormat, view::RenderLayers},
128
};
139

1410
fn main() {
@@ -33,23 +29,8 @@ fn setup(
3329
mut materials: ResMut<Assets<StandardMaterial>>,
3430
mut images: ResMut<Assets<Image>>,
3531
) {
36-
let size = Extent3d {
37-
width: 512,
38-
height: 512,
39-
..default()
40-
};
41-
4232
// This is the texture that will be rendered to.
43-
let mut image = Image::new_fill(
44-
size,
45-
TextureDimension::D2,
46-
&[0, 0, 0, 0],
47-
TextureFormat::Bgra8UnormSrgb,
48-
RenderAssetUsages::default(),
49-
);
50-
// You need to set these texture usage flags in order to use the image as a render target
51-
image.texture_descriptor.usage =
52-
TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST | TextureUsages::RENDER_ATTACHMENT;
33+
let image = Image::new_target_texture(512, 512, TextureFormat::bevy_default());
5334

5435
let image_handle = images.add(image);
5536

examples/app/headless_renderer.rs

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,16 @@ use bevy::{
1919
prelude::*,
2020
render::{
2121
camera::RenderTarget,
22-
render_asset::{RenderAssetUsages, RenderAssets},
22+
render_asset::RenderAssets,
2323
render_graph::{self, NodeRunError, RenderGraph, RenderGraphContext, RenderLabel},
2424
render_resource::{
2525
Buffer, BufferDescriptor, BufferUsages, CommandEncoderDescriptor, Extent3d, MapMode,
26-
PollType, TexelCopyBufferInfo, TexelCopyBufferLayout, TextureDimension, TextureFormat,
27-
TextureUsages,
26+
PollType, TexelCopyBufferInfo, TexelCopyBufferLayout, TextureFormat, TextureUsages,
2827
},
2928
renderer::{RenderContext, RenderDevice, RenderQueue},
3029
Extract, Render, RenderApp, RenderSystems,
3130
},
31+
window::ExitCondition,
3232
winit::WinitPlugin,
3333
};
3434
use crossbeam_channel::{Receiver, Sender};
@@ -92,7 +92,7 @@ fn main() {
9292
primary_window: None,
9393
// Don’t automatically exit due to having no windows.
9494
// Instead, the code in `update()` will explicitly produce an `AppExit` event.
95-
exit_condition: bevy::window::ExitCondition::DontExit,
95+
exit_condition: ExitCondition::DontExit,
9696
..default()
9797
})
9898
// WinitPlugin will panic in environments without a display server.
@@ -247,25 +247,14 @@ fn setup_render_target(
247247
};
248248

249249
// This is the texture that will be rendered to.
250-
let mut render_target_image = Image::new_fill(
251-
size,
252-
TextureDimension::D2,
253-
&[0; 4],
254-
TextureFormat::bevy_default(),
255-
RenderAssetUsages::default(),
256-
);
257-
render_target_image.texture_descriptor.usage |=
258-
TextureUsages::COPY_SRC | TextureUsages::RENDER_ATTACHMENT | TextureUsages::TEXTURE_BINDING;
250+
let mut render_target_image =
251+
Image::new_target_texture(size.width, size.height, TextureFormat::bevy_default());
252+
render_target_image.texture_descriptor.usage |= TextureUsages::COPY_SRC;
259253
let render_target_image_handle = images.add(render_target_image);
260254

261255
// This is the texture that will be copied to.
262-
let cpu_image = Image::new_fill(
263-
size,
264-
TextureDimension::D2,
265-
&[0; 4],
266-
TextureFormat::bevy_default(),
267-
RenderAssetUsages::default(),
268-
);
256+
let cpu_image =
257+
Image::new_target_texture(size.width, size.height, TextureFormat::bevy_default());
269258
let cpu_image_handle = images.add(cpu_image);
270259

271260
commands.spawn(ImageCopier::new(

examples/shader/compute_shader_game_of_life.rs

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -51,17 +51,8 @@ fn main() {
5151
}
5252

5353
fn setup(mut commands: Commands, mut images: ResMut<Assets<Image>>) {
54-
let mut image = Image::new_fill(
55-
Extent3d {
56-
width: SIZE.0,
57-
height: SIZE.1,
58-
depth_or_array_layers: 1,
59-
},
60-
TextureDimension::D2,
61-
&[0, 0, 0, 255],
62-
TextureFormat::R32Float,
63-
RenderAssetUsages::RENDER_WORLD,
64-
);
54+
let mut image = Image::new_target_texture(SIZE.0, SIZE.1, TextureFormat::R32Float);
55+
image.asset_usage = RenderAssetUsages::RENDER_WORLD;
6556
image.texture_descriptor.usage =
6657
TextureUsages::COPY_DST | TextureUsages::STORAGE_BINDING | TextureUsages::TEXTURE_BINDING;
6758
let image0 = images.add(image.clone());

0 commit comments

Comments
 (0)