diff --git a/crates/bevy_image/src/image.rs b/crates/bevy_image/src/image.rs index 195debc1d4..3aa96dd3b5 100644 --- a/crates/bevy_image/src/image.rs +++ b/crates/bevy_image/src/image.rs @@ -843,6 +843,58 @@ impl Image { Image::new(size, dimension, data, format, asset_usage) } + /// Create a new zero-filled image with a given size, which can be rendered to. + /// Useful for mirrors, UI, or exporting images for example. + /// This is primarily for use as a render target for a [`Camera`]. + /// See [`RenderTarget::Image`]. + /// + /// For Standard Dynamic Range (SDR) images you can use [`TextureFormat::Rgba8UnormSrgb`]. + /// For High Dynamic Range (HDR) images you can use [`TextureFormat::Rgba16Float`]. + /// + /// The default [`TextureUsages`] are + /// [`TEXTURE_BINDING`](TextureUsages::TEXTURE_BINDING), + /// [`COPY_DST`](TextureUsages::COPY_DST), + /// [`RENDER_ATTACHMENT`](TextureUsages::RENDER_ATTACHMENT). + /// + /// The default [`RenderAssetUsages`] is [`MAIN_WORLD | RENDER_WORLD`](RenderAssetUsages::default) + /// so that it is accessible from the CPU and GPU. + /// You can customize this by changing the [`asset_usage`](Image::asset_usage) field. + /// + /// [`Camera`]: https://docs.rs/bevy/latest/bevy/render/camera/struct.Camera.html + /// [`RenderTarget::Image`]: https://docs.rs/bevy/latest/bevy/render/camera/enum.RenderTarget.html#variant.Image + pub fn new_target_texture(width: u32, height: u32, format: TextureFormat) -> Self { + let size = Extent3d { + width, + height, + ..Default::default() + }; + // You need to set these texture usage flags in order to use the image as a render target + let usage = TextureUsages::TEXTURE_BINDING + | TextureUsages::COPY_DST + | TextureUsages::RENDER_ATTACHMENT; + // Fill with zeroes + let data = vec![0; format.pixel_size() * size.volume()]; + + Image { + data: Some(data), + data_order: TextureDataOrder::default(), + texture_descriptor: TextureDescriptor { + size, + format, + dimension: TextureDimension::D2, + label: None, + mip_level_count: 1, + sample_count: 1, + usage, + view_formats: &[], + }, + sampler: ImageSampler::Default, + texture_view_descriptor: None, + asset_usage: RenderAssetUsages::default(), + copy_on_resize: true, + } + } + /// Returns the width of a 2D image. #[inline] pub fn width(&self) -> u32 { diff --git a/examples/3d/render_to_texture.rs b/examples/3d/render_to_texture.rs index cf428be9c3..419c748881 100644 --- a/examples/3d/render_to_texture.rs +++ b/examples/3d/render_to_texture.rs @@ -4,11 +4,7 @@ use std::f32::consts::PI; use bevy::{ prelude::*, - render::{ - render_asset::RenderAssetUsages, - render_resource::{Extent3d, TextureDimension, TextureFormat, TextureUsages}, - view::RenderLayers, - }, + render::{render_resource::TextureFormat, view::RenderLayers}, }; fn main() { @@ -33,23 +29,8 @@ fn setup( mut materials: ResMut>, mut images: ResMut>, ) { - let size = Extent3d { - width: 512, - height: 512, - ..default() - }; - // This is the texture that will be rendered to. - let mut image = Image::new_fill( - size, - TextureDimension::D2, - &[0, 0, 0, 0], - TextureFormat::Bgra8UnormSrgb, - RenderAssetUsages::default(), - ); - // You need to set these texture usage flags in order to use the image as a render target - image.texture_descriptor.usage = - TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST | TextureUsages::RENDER_ATTACHMENT; + let image = Image::new_target_texture(512, 512, TextureFormat::bevy_default()); let image_handle = images.add(image); diff --git a/examples/app/headless_renderer.rs b/examples/app/headless_renderer.rs index 5d027b2115..a4c6ebb113 100644 --- a/examples/app/headless_renderer.rs +++ b/examples/app/headless_renderer.rs @@ -19,16 +19,16 @@ use bevy::{ prelude::*, render::{ camera::RenderTarget, - render_asset::{RenderAssetUsages, RenderAssets}, + render_asset::RenderAssets, render_graph::{self, NodeRunError, RenderGraph, RenderGraphContext, RenderLabel}, render_resource::{ Buffer, BufferDescriptor, BufferUsages, CommandEncoderDescriptor, Extent3d, MapMode, - PollType, TexelCopyBufferInfo, TexelCopyBufferLayout, TextureDimension, TextureFormat, - TextureUsages, + PollType, TexelCopyBufferInfo, TexelCopyBufferLayout, TextureFormat, TextureUsages, }, renderer::{RenderContext, RenderDevice, RenderQueue}, Extract, Render, RenderApp, RenderSystems, }, + window::ExitCondition, winit::WinitPlugin, }; use crossbeam_channel::{Receiver, Sender}; @@ -92,7 +92,7 @@ fn main() { primary_window: None, // Don’t automatically exit due to having no windows. // Instead, the code in `update()` will explicitly produce an `AppExit` event. - exit_condition: bevy::window::ExitCondition::DontExit, + exit_condition: ExitCondition::DontExit, ..default() }) // WinitPlugin will panic in environments without a display server. @@ -247,25 +247,14 @@ fn setup_render_target( }; // This is the texture that will be rendered to. - let mut render_target_image = Image::new_fill( - size, - TextureDimension::D2, - &[0; 4], - TextureFormat::bevy_default(), - RenderAssetUsages::default(), - ); - render_target_image.texture_descriptor.usage |= - TextureUsages::COPY_SRC | TextureUsages::RENDER_ATTACHMENT | TextureUsages::TEXTURE_BINDING; + let mut render_target_image = + Image::new_target_texture(size.width, size.height, TextureFormat::bevy_default()); + render_target_image.texture_descriptor.usage |= TextureUsages::COPY_SRC; let render_target_image_handle = images.add(render_target_image); // This is the texture that will be copied to. - let cpu_image = Image::new_fill( - size, - TextureDimension::D2, - &[0; 4], - TextureFormat::bevy_default(), - RenderAssetUsages::default(), - ); + let cpu_image = + Image::new_target_texture(size.width, size.height, TextureFormat::bevy_default()); let cpu_image_handle = images.add(cpu_image); commands.spawn(ImageCopier::new( diff --git a/examples/shader/compute_shader_game_of_life.rs b/examples/shader/compute_shader_game_of_life.rs index 575a09f1d1..a11767f06f 100644 --- a/examples/shader/compute_shader_game_of_life.rs +++ b/examples/shader/compute_shader_game_of_life.rs @@ -51,17 +51,8 @@ fn main() { } fn setup(mut commands: Commands, mut images: ResMut>) { - let mut image = Image::new_fill( - Extent3d { - width: SIZE.0, - height: SIZE.1, - depth_or_array_layers: 1, - }, - TextureDimension::D2, - &[0, 0, 0, 255], - TextureFormat::R32Float, - RenderAssetUsages::RENDER_WORLD, - ); + let mut image = Image::new_target_texture(SIZE.0, SIZE.1, TextureFormat::R32Float); + image.asset_usage = RenderAssetUsages::RENDER_WORLD; image.texture_descriptor.usage = TextureUsages::COPY_DST | TextureUsages::STORAGE_BINDING | TextureUsages::TEXTURE_BINDING; let image0 = images.add(image.clone());