RenderAssetPersistencePolicy → RenderAssetUsages (#11399)

# Objective

Right now, all assets in the main world get extracted and prepared in
the render world (if the asset's using the RenderAssetPlugin). This is
unfortunate for two cases:

1. **TextureAtlas** / **FontAtlas**: This one's huge. The individual
`Image` assets that make up the atlas are cloned and prepared
individually when there's no reason for them to be. The atlas textures
are built on the CPU in the main world. *There can be hundreds of images
that get prepared for rendering only not to be used.*
2. If one loads an Image and needs to transform it in a system before
rendering it, kind of like the [decompression
example](https://github.com/bevyengine/bevy/blob/main/examples/asset/asset_decompression.rs#L120),
there's a price paid for extracting & preparing the asset that's not
intended to be rendered yet.

------

* References #10520
* References #1782

## Solution

This changes the `RenderAssetPersistencePolicy` enum to bitflags. I felt
that the objective with the parameter is so similar in nature to wgpu's
[`TextureUsages`](https://docs.rs/wgpu/latest/wgpu/struct.TextureUsages.html)
and
[`BufferUsages`](https://docs.rs/wgpu/latest/wgpu/struct.BufferUsages.html),
that it may as well be just like that.

```rust
// This asset only needs to be in the main world. Don't extract and prepare it.
RenderAssetUsages::MAIN_WORLD

// Keep this asset in the main world and  
RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD

// This asset is only needed in the render world. Remove it from the asset server once extracted.
RenderAssetUsages::RENDER_WORLD
```

### Alternate Solution

I considered introducing a third field to `RenderAssetPersistencePolicy`
enum:
```rust
enum RenderAssetPersistencePolicy {
    /// Keep the asset in the main world after extracting to the render world.
    Keep,
    /// Remove the asset from the main world after extracting to the render world.
    Unload,
    /// This doesn't need to be in the render world at all.
    NoExtract, // <-----
}
```
Functional, but this seemed like shoehorning. Another option is renaming
the enum to something like:
```rust
enum RenderAssetExtractionPolicy {
    /// Extract the asset and keep it in the main world.
    Extract,
    /// Remove the asset from the main world after extracting to the render world.
    ExtractAndUnload,
    /// This doesn't need to be in the render world at all.
    NoExtract,
}
```
I think this last one could be a good option if the bitflags are too
clunky.

## Migration Guide

* `RenderAssetPersistencePolicy::Keep` → `RenderAssetUsage::MAIN_WORLD |
RenderAssetUsage::RENDER_WORLD` (or `RenderAssetUsage::default()`)
* `RenderAssetPersistencePolicy::Unload` →
`RenderAssetUsage::RENDER_WORLD`
* For types implementing the `RenderAsset` trait, change `fn
persistence_policy(&self) -> RenderAssetPersistencePolicy` to `fn
asset_usage(&self) -> RenderAssetUsages`.
* Change any references to `cpu_persistent_access`
(`RenderAssetPersistencePolicy`) to `asset_usage` (`RenderAssetUsage`).
This applies to `Image`, `Mesh`, and a few other types.
This commit is contained in:
Brian Reavis 2024-01-30 05:22:10 -08:00 committed by GitHub
parent d7c65e40ee
commit 6b40b6749e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
36 changed files with 247 additions and 143 deletions

View file

@ -6,7 +6,7 @@ use bevy_reflect::Reflect;
use bevy_render::camera::Camera;
use bevy_render::extract_component::{ExtractComponent, ExtractComponentPlugin};
use bevy_render::extract_resource::{ExtractResource, ExtractResourcePlugin};
use bevy_render::render_asset::{RenderAssetPersistencePolicy, RenderAssets};
use bevy_render::render_asset::{RenderAssetUsages, RenderAssets};
use bevy_render::render_resource::binding_types::{
sampler, texture_2d, texture_3d, uniform_buffer,
};
@ -360,7 +360,7 @@ fn setup_tonemapping_lut_image(bytes: &[u8], image_type: ImageType) -> Image {
CompressedImageFormats::NONE,
false,
image_sampler,
RenderAssetPersistencePolicy::Unload,
RenderAssetUsages::RENDER_WORLD,
)
.unwrap()
}
@ -386,6 +386,6 @@ pub fn lut_placeholder() -> Image {
},
sampler: ImageSampler::Default,
texture_view_descriptor: None,
cpu_persistent_access: RenderAssetPersistencePolicy::Unload,
asset_usage: RenderAssetUsages::RENDER_WORLD,
}
}

View file

@ -66,8 +66,7 @@ use bevy_reflect::TypePath;
use bevy_render::{
extract_component::{ComponentUniforms, DynamicUniformIndex, UniformComponentPlugin},
render_asset::{
PrepareAssetError, RenderAsset, RenderAssetPersistencePolicy, RenderAssetPlugin,
RenderAssets,
PrepareAssetError, RenderAsset, RenderAssetPlugin, RenderAssetUsages, RenderAssets,
},
render_phase::{PhaseItem, RenderCommand, RenderCommandResult, TrackedRenderPass},
render_resource::{
@ -316,8 +315,8 @@ impl RenderAsset for LineGizmo {
type PreparedAsset = GpuLineGizmo;
type Param = SRes<RenderDevice>;
fn persistence_policy(&self) -> RenderAssetPersistencePolicy {
RenderAssetPersistencePolicy::Keep
fn asset_usage(&self) -> RenderAssetUsages {
RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD
}
fn prepare_asset(

View file

@ -22,7 +22,7 @@ use bevy_render::{
},
prelude::SpatialBundle,
primitives::Aabb,
render_asset::RenderAssetPersistencePolicy,
render_asset::RenderAssetUsages,
render_resource::{Face, PrimitiveTopology},
texture::{
CompressedImageFormats, Image, ImageAddressMode, ImageFilterMode, ImageLoaderSettings,
@ -393,7 +393,7 @@ async fn load_gltf<'a, 'b, 'c>(
let primitive_label = primitive_label(&gltf_mesh, &primitive);
let primitive_topology = get_primitive_topology(primitive.mode())?;
let mut mesh = Mesh::new(primitive_topology, RenderAssetPersistencePolicy::Keep);
let mut mesh = Mesh::new(primitive_topology, RenderAssetUsages::default());
// Read vertex attributes
for (semantic, accessor) in primitive.attributes() {
@ -437,7 +437,7 @@ async fn load_gltf<'a, 'b, 'c>(
let morph_target_image = MorphTargetImage::new(
morph_target_reader.map(PrimitiveMorphAttributesIter),
mesh.count_vertices(),
RenderAssetPersistencePolicy::Keep,
RenderAssetUsages::default(),
)?;
let handle =
load_context.add_labeled_asset(morph_targets_label, morph_target_image.0);
@ -744,7 +744,7 @@ async fn load_image<'a, 'b>(
supported_compressed_formats,
is_srgb,
ImageSampler::Descriptor(sampler_descriptor),
RenderAssetPersistencePolicy::Keep,
RenderAssetUsages::default(),
)?;
Ok(ImageOrPath::Image {
image,
@ -766,7 +766,7 @@ async fn load_image<'a, 'b>(
supported_compressed_formats,
is_srgb,
ImageSampler::Descriptor(sampler_descriptor),
RenderAssetPersistencePolicy::Keep,
RenderAssetUsages::default(),
)?,
label: texture_label(&gltf_texture),
})

View file

@ -6,7 +6,7 @@ pub use wgpu::PrimitiveTopology;
use crate::{
prelude::Image,
primitives::Aabb,
render_asset::{PrepareAssetError, RenderAsset, RenderAssetPersistencePolicy, RenderAssets},
render_asset::{PrepareAssetError, RenderAsset, RenderAssetUsages, RenderAssets},
render_resource::{Buffer, TextureView, VertexBufferLayout},
renderer::RenderDevice,
};
@ -49,10 +49,10 @@ pub const VERTEX_ATTRIBUTE_BUFFER_ID: u64 = 10;
/// ```
/// # use bevy_render::mesh::{Mesh, Indices};
/// # use bevy_render::render_resource::PrimitiveTopology;
/// # use bevy_render::render_asset::RenderAssetPersistencePolicy;
/// # use bevy_render::render_asset::RenderAssetUsages;
/// fn create_simple_parallelogram() -> Mesh {
/// // Create a new mesh using a triangle list topology, where each set of 3 vertices composes a triangle.
/// Mesh::new(PrimitiveTopology::TriangleList, RenderAssetPersistencePolicy::Unload)
/// Mesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::default())
/// // Add 4 vertices, each with its own position attribute (coordinate in
/// // 3D space), for each of the corners of the parallelogram.
/// .with_inserted_attribute(
@ -123,7 +123,7 @@ pub struct Mesh {
indices: Option<Indices>,
morph_targets: Option<Handle<Image>>,
morph_target_names: Option<Vec<String>>,
pub cpu_persistent_access: RenderAssetPersistencePolicy,
pub asset_usage: RenderAssetUsages,
}
impl Mesh {
@ -184,17 +184,14 @@ impl Mesh {
/// Construct a new mesh. You need to provide a [`PrimitiveTopology`] so that the
/// renderer knows how to treat the vertex data. Most of the time this will be
/// [`PrimitiveTopology::TriangleList`].
pub fn new(
primitive_topology: PrimitiveTopology,
cpu_persistent_access: RenderAssetPersistencePolicy,
) -> Self {
pub fn new(primitive_topology: PrimitiveTopology, asset_usage: RenderAssetUsages) -> Self {
Mesh {
primitive_topology,
attributes: Default::default(),
indices: None,
morph_targets: None,
morph_target_names: None,
cpu_persistent_access,
asset_usage,
}
}
@ -1148,8 +1145,8 @@ impl RenderAsset for Mesh {
type PreparedAsset = GpuMesh;
type Param = (SRes<RenderDevice>, SRes<RenderAssets<Image>>);
fn persistence_policy(&self) -> RenderAssetPersistencePolicy {
self.cpu_persistent_access
fn asset_usage(&self) -> RenderAssetUsages {
self.asset_usage
}
/// Converts the extracted mesh a into [`GpuMesh`].
@ -1317,7 +1314,7 @@ fn generate_tangents_for_mesh(mesh: &Mesh) -> Result<Vec<[f32; 4]>, GenerateTang
#[cfg(test)]
mod tests {
use super::Mesh;
use crate::render_asset::RenderAssetPersistencePolicy;
use crate::render_asset::RenderAssetUsages;
use wgpu::PrimitiveTopology;
#[test]
@ -1325,7 +1322,7 @@ mod tests {
fn panic_invalid_format() {
let _mesh = Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetPersistencePolicy::Unload,
RenderAssetUsages::default(),
)
.with_inserted_attribute(Mesh::ATTRIBUTE_UV_0, vec![[0.0, 0.0, 0.0]]);
}

View file

@ -1,6 +1,6 @@
use crate::{
mesh::Mesh,
render_asset::RenderAssetPersistencePolicy,
render_asset::RenderAssetUsages,
render_resource::{Extent3d, TextureDimension, TextureFormat},
texture::Image,
};
@ -68,7 +68,7 @@ impl MorphTargetImage {
pub fn new(
targets: impl ExactSizeIterator<Item = impl Iterator<Item = MorphAttributes>>,
vertex_count: usize,
cpu_persistent_access: RenderAssetPersistencePolicy,
asset_usage: RenderAssetUsages,
) -> Result<Self, MorphBuildError> {
let max = MAX_TEXTURE_WIDTH;
let target_count = targets.len();
@ -108,7 +108,7 @@ impl MorphTargetImage {
TextureDimension::D3,
data,
TextureFormat::R32Float,
cpu_persistent_access,
asset_usage,
);
Ok(MorphTargetImage(image))
}

View file

@ -1,6 +1,6 @@
use crate::{
mesh::{Indices, Mesh},
render_asset::RenderAssetPersistencePolicy,
render_asset::RenderAssetUsages,
};
use super::Meshable;
@ -161,7 +161,7 @@ impl EllipseMeshBuilder {
Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetPersistencePolicy::Keep,
RenderAssetUsages::default(),
)
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
@ -220,7 +220,7 @@ impl Meshable for Triangle2d {
Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetPersistencePolicy::Keep,
RenderAssetUsages::default(),
)
.with_indices(Some(indices))
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
@ -252,7 +252,7 @@ impl Meshable for Rectangle {
Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetPersistencePolicy::Keep,
RenderAssetUsages::default(),
)
.with_indices(Some(indices))
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)

View file

@ -1,6 +1,6 @@
use crate::{
mesh::{Indices, Mesh},
render_asset::RenderAssetPersistencePolicy,
render_asset::RenderAssetUsages,
};
use bevy_math::{Vec2, Vec3};
use wgpu::PrimitiveTopology;
@ -369,7 +369,7 @@ impl From<Capsule> for Mesh {
Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetPersistencePolicy::Keep,
RenderAssetUsages::default(),
)
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vs)
.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, vns)

View file

@ -1,6 +1,6 @@
use crate::{
mesh::{Indices, Mesh},
render_asset::RenderAssetPersistencePolicy,
render_asset::RenderAssetUsages,
};
use wgpu::PrimitiveTopology;
@ -123,7 +123,7 @@ impl From<Cylinder> for Mesh {
Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetPersistencePolicy::Keep,
RenderAssetUsages::default(),
)
.with_indices(Some(Indices::U32(indices)))
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)

View file

@ -1,6 +1,6 @@
use crate::{
mesh::{Indices, Mesh},
render_asset::RenderAssetPersistencePolicy,
render_asset::RenderAssetUsages,
};
use hexasphere::shapes::IcoSphere;
use thiserror::Error;
@ -108,7 +108,7 @@ impl TryFrom<Icosphere> for Mesh {
Ok(Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetPersistencePolicy::Keep,
RenderAssetUsages::default(),
)
.with_indices(Some(indices))
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, points)

View file

@ -1,4 +1,4 @@
use crate::render_asset::RenderAssetPersistencePolicy;
use crate::render_asset::RenderAssetUsages;
use super::{Indices, Mesh};
use bevy_math::*;
@ -124,7 +124,7 @@ impl From<Box> for Mesh {
Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetPersistencePolicy::Keep,
RenderAssetUsages::default(),
)
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)
@ -179,7 +179,7 @@ impl From<Quad> for Mesh {
Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetPersistencePolicy::Keep,
RenderAssetUsages::default(),
)
.with_indices(Some(indices))
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
@ -263,7 +263,7 @@ impl From<Plane> for Mesh {
Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetPersistencePolicy::Keep,
RenderAssetUsages::default(),
)
.with_indices(Some(Indices::U32(indices)))
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)

View file

@ -1,6 +1,6 @@
use crate::{
mesh::{Indices, Mesh},
render_asset::RenderAssetPersistencePolicy,
render_asset::RenderAssetUsages,
};
use wgpu::PrimitiveTopology;
@ -60,7 +60,7 @@ impl From<RegularPolygon> for Mesh {
Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetPersistencePolicy::Keep,
RenderAssetUsages::default(),
)
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
.with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, normals)

View file

@ -1,6 +1,6 @@
use crate::{
mesh::{Indices, Mesh},
render_asset::RenderAssetPersistencePolicy,
render_asset::RenderAssetUsages,
};
use bevy_math::Vec3;
use wgpu::PrimitiveTopology;
@ -89,7 +89,7 @@ impl From<Torus> for Mesh {
Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetPersistencePolicy::Keep,
RenderAssetUsages::default(),
)
.with_indices(Some(Indices::U32(indices)))
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)

View file

@ -2,7 +2,7 @@ use wgpu::PrimitiveTopology;
use crate::{
mesh::{Indices, Mesh},
render_asset::RenderAssetPersistencePolicy,
render_asset::RenderAssetUsages,
};
use std::f32::consts::PI;
@ -85,7 +85,7 @@ impl From<UVSphere> for Mesh {
Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetPersistencePolicy::Keep,
RenderAssetUsages::default(),
)
.with_indices(Some(Indices::U32(indices)))
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vertices)

View file

@ -7,7 +7,11 @@ use bevy_ecs::{
system::{StaticSystemParam, SystemParam, SystemParamItem, SystemState},
world::{FromWorld, Mut},
};
use bevy_reflect::Reflect;
use bevy_reflect::{
utility::{reflect_hasher, NonGenericTypeInfoCell},
FromReflect, Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, TypePath, Typed,
ValueInfo,
};
use bevy_utils::{thiserror::Error, HashMap, HashSet};
use serde::{Deserialize, Serialize};
use std::marker::PhantomData;
@ -35,7 +39,7 @@ pub trait RenderAsset: Asset + Clone {
type Param: SystemParam;
/// Whether or not to unload the asset after extracting it to the render world.
fn persistence_policy(&self) -> RenderAssetPersistencePolicy;
fn asset_usage(&self) -> RenderAssetUsages;
/// Prepares the asset for the GPU by transforming it into a [`RenderAsset::PreparedAsset`].
///
@ -46,19 +50,120 @@ pub trait RenderAsset: Asset + Clone {
) -> Result<Self::PreparedAsset, PrepareAssetError<Self>>;
}
/// Whether or not to unload the [`RenderAsset`] after extracting it to the render world.
///
/// Unloading the asset saves on memory, as for most cases it is no longer necessary to keep
/// it in RAM once it's been uploaded to the GPU's VRAM. However, this means you can no longer
/// access the asset from the CPU (via the `Assets<T>` resource) once unloaded (without re-loading it).
///
/// If you never need access to the asset from the CPU past the first frame it's loaded on,
/// or only need very infrequent access, then set this to Unload. Otherwise, set this to Keep.
#[derive(Reflect, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Default, Debug)]
pub enum RenderAssetPersistencePolicy {
Unload,
#[default]
Keep,
bitflags::bitflags! {
/// Defines where the asset will be used.
///
/// If an asset is set to the `RENDER_WORLD` but not the `MAIN_WORLD`, the asset will be
/// unloaded from the asset server once it's been extracted and prepared in the render world.
///
/// Unloading the asset saves on memory, as for most cases it is no longer necessary to keep
/// it in RAM once it's been uploaded to the GPU's VRAM. However, this means you can no longer
/// access the asset from the CPU (via the `Assets<T>` resource) once unloaded (without re-loading it).
///
/// If you never need access to the asset from the CPU past the first frame it's loaded on,
/// or only need very infrequent access, then set this to `RENDER_WORLD`. Otherwise, set this to
/// `RENDER_WORLD | MAIN_WORLD`.
///
/// If you have an asset that doesn't actually need to end up in the render world, like an Image
/// that will be decoded into another Image asset, use `MAIN_WORLD` only.
#[repr(transparent)]
#[derive(Serialize, TypePath, Deserialize, Hash, Clone, Copy, PartialEq, Eq, Debug)]
pub struct RenderAssetUsages: u8 {
const MAIN_WORLD = 1 << 0;
const RENDER_WORLD = 1 << 1;
}
}
impl Default for RenderAssetUsages {
/// Returns the default render asset usage flags:
/// `RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD`
///
/// This default configuration ensures the asset persists in the main world, even after being prepared for rendering.
///
/// If your asset does not change, consider using `RenderAssetUsages::RENDER_WORLD` exclusively. This will cause
/// the asset to be unloaded from the main world once it has been prepared for rendering. If the asset does not need
/// to reach the render world at all, use `RenderAssetUsages::MAIN_WORLD` exclusively.
fn default() -> Self {
RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD
}
}
impl Reflect for RenderAssetUsages {
fn get_represented_type_info(&self) -> Option<&'static bevy_reflect::TypeInfo> {
Some(<Self as Typed>::type_info())
}
fn into_any(self: Box<Self>) -> Box<dyn std::any::Any> {
self
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn into_reflect(self: Box<Self>) -> Box<dyn Reflect> {
self
}
fn as_reflect(&self) -> &dyn Reflect {
self
}
fn as_reflect_mut(&mut self) -> &mut dyn Reflect {
self
}
fn apply(&mut self, value: &dyn Reflect) {
let value = value.as_any();
if let Some(&value) = value.downcast_ref::<Self>() {
*self = value;
} else {
panic!("Value is not a {}.", Self::type_path());
}
}
fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> {
*self = value.take()?;
Ok(())
}
fn reflect_ref(&self) -> bevy_reflect::ReflectRef {
ReflectRef::Value(self)
}
fn reflect_mut(&mut self) -> bevy_reflect::ReflectMut {
ReflectMut::Value(self)
}
fn reflect_owned(self: Box<Self>) -> bevy_reflect::ReflectOwned {
ReflectOwned::Value(self)
}
fn clone_value(&self) -> Box<dyn Reflect> {
Box::new(*self)
}
fn reflect_hash(&self) -> Option<u64> {
use std::hash::Hash;
use std::hash::Hasher;
let mut hasher = reflect_hasher();
Hash::hash(&std::any::Any::type_id(self), &mut hasher);
Hash::hash(self, &mut hasher);
Some(hasher.finish())
}
fn reflect_partial_eq(&self, value: &dyn Reflect) -> Option<bool> {
let value = value.as_any();
if let Some(value) = value.downcast_ref::<Self>() {
Some(std::cmp::PartialEq::eq(self, value))
} else {
Some(false)
}
}
}
impl FromReflect for RenderAssetUsages {
fn from_reflect(reflect: &dyn Reflect) -> Option<Self> {
let raw_value = *reflect.as_any().downcast_ref::<u8>()?;
Self::from_bits(raw_value)
}
}
impl Typed for RenderAssetUsages {
fn type_info() -> &'static TypeInfo {
static CELL: NonGenericTypeInfoCell = NonGenericTypeInfoCell::new();
CELL.get_or_set(|| TypeInfo::Value(ValueInfo::new::<Self>()))
}
}
/// This plugin extracts the changed assets from the "app world" into the "render world"
@ -224,12 +329,15 @@ fn extract_render_asset<A: RenderAsset>(mut commands: Commands, mut main_world:
let mut extracted_assets = Vec::new();
for id in changed_assets.drain() {
if let Some(asset) = assets.get(id) {
if asset.persistence_policy() == RenderAssetPersistencePolicy::Unload {
if let Some(asset) = assets.remove(id) {
extracted_assets.push((id, asset));
let asset_usage = asset.asset_usage();
if asset_usage.contains(RenderAssetUsages::RENDER_WORLD) {
if asset_usage == RenderAssetUsages::RENDER_WORLD {
if let Some(asset) = assets.remove(id) {
extracted_assets.push((id, asset));
}
} else {
extracted_assets.push((id, asset.clone()));
}
} else {
extracted_assets.push((id, asset.clone()));
}
}
}

View file

@ -56,7 +56,7 @@ impl AssetSaver for CompressedImageSaver {
format: ImageFormatSetting::Format(ImageFormat::Basis),
is_srgb,
sampler: image.sampler.clone(),
cpu_persistent_access: image.cpu_persistent_access,
asset_usage: image.asset_usage,
})
}
.boxed()

View file

@ -1,5 +1,5 @@
use crate::{
render_asset::RenderAssetPersistencePolicy,
render_asset::RenderAssetUsages,
texture::{Image, TextureFormatPixelInfo},
};
use bevy_asset::{
@ -18,7 +18,7 @@ pub struct ExrTextureLoader;
#[derive(Serialize, Deserialize, Default, Debug)]
pub struct ExrTextureLoaderSettings {
pub cpu_persistent_access: RenderAssetPersistencePolicy,
pub asset_usage: RenderAssetUsages,
}
/// Possible errors that can be produced by [`ExrTextureLoader`]
@ -72,7 +72,7 @@ impl AssetLoader for ExrTextureLoader {
TextureDimension::D2,
buf,
format,
settings.cpu_persistent_access,
settings.asset_usage,
))
})
}

View file

@ -1,6 +1,4 @@
use crate::{
render_asset::RenderAssetPersistencePolicy, render_resource::*, texture::DefaultImageSampler,
};
use crate::{render_asset::RenderAssetUsages, render_resource::*, texture::DefaultImageSampler};
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{
prelude::{FromWorld, Res, ResMut},
@ -83,7 +81,7 @@ fn fallback_image_new(
image_dimension,
&data,
format,
RenderAssetPersistencePolicy::Unload,
RenderAssetUsages::RENDER_WORLD,
)
} else {
let mut image = Image::default();

View file

@ -1,5 +1,5 @@
use crate::{
render_asset::RenderAssetPersistencePolicy,
render_asset::RenderAssetUsages,
texture::{Image, TextureFormatPixelInfo},
};
use bevy_asset::{io::Reader, AssetLoader, AsyncReadExt, LoadContext};
@ -13,7 +13,7 @@ pub struct HdrTextureLoader;
#[derive(Serialize, Deserialize, Default, Debug)]
pub struct HdrTextureLoaderSettings {
pub cpu_persistent_access: RenderAssetPersistencePolicy,
pub asset_usage: RenderAssetUsages,
}
#[non_exhaustive]
@ -68,7 +68,7 @@ impl AssetLoader for HdrTextureLoader {
TextureDimension::D2,
rgba_data,
format,
settings.cpu_persistent_access,
settings.asset_usage,
))
})
}

View file

@ -6,7 +6,7 @@ use super::dds::*;
use super::ktx2::*;
use crate::{
render_asset::{PrepareAssetError, RenderAsset, RenderAssetPersistencePolicy},
render_asset::{PrepareAssetError, RenderAsset, RenderAssetUsages},
render_resource::{Sampler, Texture, TextureView},
renderer::{RenderDevice, RenderQueue},
texture::BevyDefault,
@ -110,7 +110,7 @@ pub struct Image {
/// The [`ImageSampler`] to use during rendering.
pub sampler: ImageSampler,
pub texture_view_descriptor: Option<TextureViewDescriptor<'static>>,
pub cpu_persistent_access: RenderAssetPersistencePolicy,
pub asset_usage: RenderAssetUsages,
}
/// Used in [`Image`], this determines what image sampler to use when rendering. The default setting,
@ -467,7 +467,7 @@ impl Default for Image {
},
sampler: ImageSampler::Default,
texture_view_descriptor: None,
cpu_persistent_access: RenderAssetPersistencePolicy::Keep,
asset_usage: RenderAssetUsages::default(),
}
}
}
@ -483,7 +483,7 @@ impl Image {
dimension: TextureDimension,
data: Vec<u8>,
format: TextureFormat,
cpu_persistent_access: RenderAssetPersistencePolicy,
asset_usage: RenderAssetUsages,
) -> Self {
debug_assert_eq!(
size.volume() * format.pixel_size(),
@ -497,7 +497,7 @@ impl Image {
image.texture_descriptor.dimension = dimension;
image.texture_descriptor.size = size;
image.texture_descriptor.format = format;
image.cpu_persistent_access = cpu_persistent_access;
image.asset_usage = asset_usage;
image
}
@ -511,12 +511,12 @@ impl Image {
dimension: TextureDimension,
pixel: &[u8],
format: TextureFormat,
cpu_persistent_access: RenderAssetPersistencePolicy,
asset_usage: RenderAssetUsages,
) -> Self {
let mut value = Image::default();
value.texture_descriptor.format = format;
value.texture_descriptor.dimension = dimension;
value.cpu_persistent_access = cpu_persistent_access;
value.asset_usage = asset_usage;
value.resize(size);
debug_assert_eq!(
@ -637,9 +637,7 @@ impl Image {
}
_ => None,
})
.map(|(dyn_img, is_srgb)| {
Self::from_dynamic(dyn_img, is_srgb, self.cpu_persistent_access)
})
.map(|(dyn_img, is_srgb)| Self::from_dynamic(dyn_img, is_srgb, self.asset_usage))
}
/// Load a bytes buffer in a [`Image`], according to type `image_type`, using the `image`
@ -650,7 +648,7 @@ impl Image {
#[allow(unused_variables)] supported_compressed_formats: CompressedImageFormats,
is_srgb: bool,
image_sampler: ImageSampler,
cpu_persistent_access: RenderAssetPersistencePolicy,
asset_usage: RenderAssetUsages,
) -> Result<Image, TextureError> {
let format = image_type.to_image_format()?;
@ -679,7 +677,7 @@ impl Image {
reader.set_format(image_crate_format);
reader.no_limits();
let dyn_img = reader.decode()?;
Self::from_dynamic(dyn_img, is_srgb, cpu_persistent_access)
Self::from_dynamic(dyn_img, is_srgb, asset_usage)
}
};
image.sampler = image_sampler;
@ -819,8 +817,8 @@ impl RenderAsset for Image {
SRes<DefaultImageSampler>,
);
fn persistence_policy(&self) -> RenderAssetPersistencePolicy {
self.cpu_persistent_access
fn asset_usage(&self) -> RenderAssetUsages {
self.asset_usage
}
/// Converts the extracted image into a [`GpuImage`].
@ -926,7 +924,7 @@ impl CompressedImageFormats {
mod test {
use super::*;
use crate::render_asset::RenderAssetPersistencePolicy;
use crate::render_asset::RenderAssetUsages;
#[test]
fn image_size() {
@ -940,7 +938,7 @@ mod test {
TextureDimension::D2,
&[0, 0, 0, 255],
TextureFormat::Rgba8Unorm,
RenderAssetPersistencePolicy::Unload,
RenderAssetUsages::MAIN_WORLD,
);
assert_eq!(
Vec2::new(size.width as f32, size.height as f32),

View file

@ -3,7 +3,7 @@ use bevy_ecs::prelude::{FromWorld, World};
use thiserror::Error;
use crate::{
render_asset::RenderAssetPersistencePolicy,
render_asset::RenderAssetUsages,
renderer::RenderDevice,
texture::{Image, ImageFormat, ImageType, TextureError},
};
@ -58,7 +58,7 @@ pub struct ImageLoaderSettings {
pub format: ImageFormatSetting,
pub is_srgb: bool,
pub sampler: ImageSampler,
pub cpu_persistent_access: RenderAssetPersistencePolicy,
pub asset_usage: RenderAssetUsages,
}
impl Default for ImageLoaderSettings {
@ -67,7 +67,7 @@ impl Default for ImageLoaderSettings {
format: ImageFormatSetting::default(),
is_srgb: true,
sampler: ImageSampler::Default,
cpu_persistent_access: RenderAssetPersistencePolicy::Keep,
asset_usage: RenderAssetUsages::default(),
}
}
}
@ -107,7 +107,7 @@ impl AssetLoader for ImageLoader {
self.supported_compressed_formats,
settings.is_srgb,
settings.sampler.clone(),
settings.cpu_persistent_access,
settings.asset_usage,
)
.map_err(|err| FileTextureError {
error: err,

View file

@ -1,5 +1,5 @@
use crate::{
render_asset::RenderAssetPersistencePolicy,
render_asset::RenderAssetUsages,
texture::{Image, TextureFormatPixelInfo},
};
use image::{DynamicImage, ImageBuffer};
@ -11,7 +11,7 @@ impl Image {
pub fn from_dynamic(
dyn_img: DynamicImage,
is_srgb: bool,
cpu_persistent_access: RenderAssetPersistencePolicy,
asset_usage: RenderAssetUsages,
) -> Image {
use bevy_core::cast_slice;
let width;
@ -158,7 +158,7 @@ impl Image {
TextureDimension::D2,
data,
format,
cpu_persistent_access,
asset_usage,
)
}
@ -222,7 +222,7 @@ mod test {
use image::{GenericImage, Rgba};
use super::*;
use crate::render_asset::RenderAssetPersistencePolicy;
use crate::render_asset::RenderAssetUsages;
#[test]
fn two_way_conversion() {
@ -230,8 +230,7 @@ mod test {
let mut initial = DynamicImage::new_rgba8(1, 1);
initial.put_pixel(0, 0, Rgba::from([132, 3, 7, 200]));
let image =
Image::from_dynamic(initial.clone(), true, RenderAssetPersistencePolicy::Unload);
let image = Image::from_dynamic(initial.clone(), true, RenderAssetUsages::RENDER_WORLD);
// NOTE: Fails if `is_srbg = false` or the dynamic image is of the type rgb8.
assert_eq!(initial, image.try_into_dynamic().unwrap());

View file

@ -14,7 +14,7 @@ use wgpu::{
use crate::{
prelude::{Image, Shader},
render_asset::RenderAssetPersistencePolicy,
render_asset::RenderAssetUsages,
render_resource::{
binding_types::texture_2d, BindGroup, BindGroupLayout, BindGroupLayoutEntries, Buffer,
CachedRenderPipelineId, FragmentState, PipelineCache, RenderPipelineDescriptor,
@ -364,7 +364,7 @@ pub(crate) fn collect_screenshots(world: &mut World) {
wgpu::TextureDimension::D2,
result,
texture_format,
RenderAssetPersistencePolicy::Unload,
RenderAssetUsages::RENDER_WORLD,
));
};

View file

@ -2,7 +2,7 @@ use crate::TextureAtlasLayout;
use bevy_asset::{Assets, Handle};
use bevy_math::{IVec2, Rect, Vec2};
use bevy_render::{
render_asset::RenderAssetPersistencePolicy,
render_asset::{RenderAsset, RenderAssetUsages},
texture::{Image, TextureFormatPixelInfo},
};
use guillotiere::{size2, Allocation, AtlasAllocator};
@ -30,10 +30,11 @@ impl DynamicTextureAtlasBuilder {
}
}
/// Add a new texture to `atlas_layout`
/// It is the user's responsibility to pass in the correct [`TextureAtlasLayout`]
/// and that `atlas_texture_handle` has [`Image::cpu_persistent_access`]
/// set to [`RenderAssetPersistencePolicy::Keep`]
/// Add a new texture to `atlas_layout`.
///
/// It is the user's responsibility to pass in the correct [`TextureAtlasLayout`].
/// Also, the asset that `atlas_texture_handle` points to must have a usage matching
/// [`RenderAssetUsages::MAIN_WORLD`].
///
/// # Arguments
///
@ -54,9 +55,11 @@ impl DynamicTextureAtlasBuilder {
));
if let Some(allocation) = allocation {
let atlas_texture = textures.get_mut(atlas_texture_handle).unwrap();
assert_eq!(
atlas_texture.cpu_persistent_access,
RenderAssetPersistencePolicy::Keep
assert!(
atlas_texture
.asset_usage()
.contains(RenderAssetUsages::MAIN_WORLD),
"The asset at atlas_texture_handle must have the RenderAssetUsages::MAIN_WORLD usage flag set"
);
self.place_texture(atlas_texture, allocation, texture);

View file

@ -2,7 +2,7 @@ use bevy_asset::AssetId;
use bevy_log::{debug, error, warn};
use bevy_math::{Rect, UVec2, Vec2};
use bevy_render::{
render_asset::RenderAssetPersistencePolicy,
render_asset::RenderAssetUsages,
render_resource::{Extent3d, TextureDimension, TextureFormat},
texture::{Image, TextureFormatPixelInfo},
};
@ -241,7 +241,7 @@ impl<'a> TextureAtlasBuilder<'a> {
self.format.pixel_size() * (current_width * current_height) as usize
],
self.format,
RenderAssetPersistencePolicy::Keep,
RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD,
);
Some(rect_placements)
}

View file

@ -2,7 +2,7 @@ use ab_glyph::{FontArc, FontVec, InvalidFont, OutlinedGlyph};
use bevy_asset::Asset;
use bevy_reflect::TypePath;
use bevy_render::{
render_asset::RenderAssetPersistencePolicy,
render_asset::RenderAssetUsages,
render_resource::{Extent3d, TextureDimension, TextureFormat},
texture::Image,
};
@ -45,7 +45,9 @@ impl Font {
.flat_map(|a| vec![255, 255, 255, (*a * 255.0) as u8])
.collect::<Vec<u8>>(),
TextureFormat::Rgba8UnormSrgb,
RenderAssetPersistencePolicy::Unload,
// This glyph image never needs to reach the render world because it's placed
// into a font texture atlas that'll be used for rendering.
RenderAssetUsages::MAIN_WORLD,
)
}
}

View file

@ -2,7 +2,7 @@ use ab_glyph::{GlyphId, Point};
use bevy_asset::{Assets, Handle};
use bevy_math::Vec2;
use bevy_render::{
render_asset::RenderAssetPersistencePolicy,
render_asset::RenderAssetUsages,
render_resource::{Extent3d, TextureDimension, TextureFormat},
texture::Image,
};
@ -63,7 +63,7 @@ impl FontAtlas {
&[0, 0, 0, 0],
TextureFormat::Rgba8UnormSrgb,
// Need to keep this image CPU persistent in order to add additional glyphs later on
RenderAssetPersistencePolicy::Keep,
RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD,
));
let texture_atlas = TextureAtlasLayout::new_empty(size);
Self {

View file

@ -10,7 +10,7 @@ use bevy::{
prelude::*,
render::{
mesh::{Indices, MeshVertexAttribute},
render_asset::RenderAssetPersistencePolicy,
render_asset::RenderAssetUsages,
render_asset::RenderAssets,
render_phase::{AddRenderCommand, DrawFunctions, RenderPhase, SetItemPipeline},
render_resource::{
@ -48,12 +48,12 @@ fn star(
// We will specify here what kind of topology is used to define the mesh,
// that is, how triangles are built from the vertices. We will use a
// triangle list, meaning that each vertex of the triangle has to be
// specified. We set `cpu_persistent_access` to unload, meaning this mesh
// specified. We set `RenderAssetUsages::RENDER_WORLD`, meaning this mesh
// will not be accessible in future frames from the `meshes` resource, in
// order to save on memory once it has been uploaded to the GPU.
let mut star = Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetPersistencePolicy::Unload,
RenderAssetUsages::RENDER_WORLD,
);
// Vertices need to have a position attribute. We will use the following

View file

@ -6,7 +6,7 @@ use std::f32::consts::PI;
use bevy::{
prelude::*,
render::{
render_asset::RenderAssetPersistencePolicy,
render_asset::RenderAssetUsages,
render_resource::{Extent3d, TextureDimension, TextureFormat},
},
};
@ -120,6 +120,6 @@ fn uv_debug_texture() -> Image {
TextureDimension::D2,
&texture_data,
TextureFormat::Rgba8UnormSrgb,
RenderAssetPersistencePolicy::Unload,
RenderAssetUsages::RENDER_WORLD,
)
}

View file

@ -13,7 +13,7 @@ use bevy::{
pbr::CascadeShadowConfigBuilder,
prelude::*,
render::{
render_asset::RenderAssetPersistencePolicy,
render_asset::RenderAssetUsages,
render_resource::{Extent3d, TextureDimension, TextureFormat},
texture::{ImageSampler, ImageSamplerDescriptor},
},
@ -381,7 +381,7 @@ fn uv_debug_texture() -> Image {
TextureDimension::D2,
&texture_data,
TextureFormat::Rgba8UnormSrgb,
RenderAssetPersistencePolicy::Unload,
RenderAssetUsages::RENDER_WORLD,
);
img.sampler = ImageSampler::Descriptor(ImageSamplerDescriptor::default());
img

View file

@ -4,7 +4,7 @@
use bevy::prelude::*;
use bevy::render::{
mesh::{Indices, VertexAttributeValues},
render_asset::RenderAssetPersistencePolicy,
render_asset::RenderAssetUsages,
render_resource::PrimitiveTopology,
};
@ -123,7 +123,7 @@ fn input_handler(
#[rustfmt::skip]
fn create_cube_mesh() -> Mesh {
// Keep the mesh data accessible in future frames to be able to mutate it in toggle_texture.
Mesh::new(PrimitiveTopology::TriangleList, RenderAssetPersistencePolicy::Keep)
Mesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD)
.with_inserted_attribute(
Mesh::ATTRIBUTE_POSITION,
// Each array is an [x, y, z] coordinate in local space.

View file

@ -6,7 +6,7 @@ use bevy::{
reflect::TypePath,
render::{
mesh::{MeshVertexBufferLayout, PrimitiveTopology},
render_asset::RenderAssetPersistencePolicy,
render_asset::RenderAssetUsages,
render_resource::{
AsBindGroup, PolygonMode, RenderPipelineDescriptor, ShaderRef,
SpecializedMeshPipelineError,
@ -99,7 +99,7 @@ impl From<LineList> for Mesh {
// This tells wgpu that the positions are list of lines
// where every pair is a start and end point
PrimitiveTopology::LineList,
RenderAssetPersistencePolicy::Unload,
RenderAssetUsages::RENDER_WORLD,
)
// Add the vertices positions as an attribute
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, vertices)
@ -118,7 +118,7 @@ impl From<LineStrip> for Mesh {
// This tells wgpu that the positions are a list of points
// where a line will be drawn between each consecutive point
PrimitiveTopology::LineStrip,
RenderAssetPersistencePolicy::Unload,
RenderAssetUsages::RENDER_WORLD,
)
// Add the point positions as an attribute
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, line.points)

View file

@ -7,7 +7,7 @@ use bevy::{
prelude::*,
reflect::TypePath,
render::{
render_asset::RenderAssetPersistencePolicy,
render_asset::RenderAssetUsages,
render_resource::{AsBindGroup, Extent3d, ShaderRef, TextureDimension, TextureFormat},
texture::{ImageSampler, ImageSamplerDescriptor},
view::ColorGrading,
@ -691,7 +691,7 @@ fn uv_debug_texture() -> Image {
TextureDimension::D2,
&texture_data,
TextureFormat::Rgba8UnormSrgb,
RenderAssetPersistencePolicy::Unload,
RenderAssetUsages::RENDER_WORLD,
);
img.sampler = ImageSampler::Descriptor(ImageSamplerDescriptor::default());
img

View file

@ -11,7 +11,7 @@ use bevy::{
skinning::{SkinnedMesh, SkinnedMeshInverseBindposes},
Indices, PrimitiveTopology, VertexAttributeValues,
},
render_asset::RenderAssetPersistencePolicy,
render_asset::RenderAssetUsages,
},
};
use rand::{rngs::StdRng, Rng, SeedableRng};
@ -56,7 +56,7 @@ fn setup(
// Create a mesh
let mesh = Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetPersistencePolicy::Unload,
RenderAssetUsages::RENDER_WORLD,
)
// Set mesh vertex positions
.with_inserted_attribute(

View file

@ -7,7 +7,7 @@ use bevy::{
prelude::*,
render::{
extract_resource::{ExtractResource, ExtractResourcePlugin},
render_asset::RenderAssetPersistencePolicy,
render_asset::RenderAssetUsages,
render_asset::RenderAssets,
render_graph::{self, RenderGraph},
render_resource::*,
@ -49,7 +49,7 @@ fn setup(mut commands: Commands, mut images: ResMut<Assets<Image>>) {
TextureDimension::D2,
&[0, 0, 0, 255],
TextureFormat::Rgba8Unorm,
RenderAssetPersistencePolicy::Unload,
RenderAssetUsages::RENDER_WORLD,
);
image.texture_descriptor.usage =
TextureUsages::COPY_DST | TextureUsages::STORAGE_BINDING | TextureUsages::TEXTURE_BINDING;

View file

@ -9,7 +9,7 @@ use bevy::{
diagnostic::{DiagnosticsStore, FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
prelude::*,
render::{
render_asset::RenderAssetPersistencePolicy,
render_asset::RenderAssetUsages,
render_resource::{Extent3d, TextureDimension, TextureFormat},
},
sprite::{MaterialMesh2dBundle, Mesh2dHandle},
@ -547,7 +547,7 @@ fn init_textures(textures: &mut Vec<Handle<Image>>, args: &Args, images: &mut As
TextureDimension::D2,
&pixel,
TextureFormat::Rgba8UnormSrgb,
RenderAssetPersistencePolicy::Unload,
RenderAssetUsages::RENDER_WORLD,
)));
}
}

View file

@ -16,7 +16,7 @@ use bevy::{
math::{DVec2, DVec3},
prelude::*,
render::{
render_asset::RenderAssetPersistencePolicy,
render_asset::RenderAssetUsages,
render_resource::{Extent3d, TextureDimension, TextureFormat},
},
window::{PresentMode, WindowPlugin, WindowResolution},
@ -205,7 +205,7 @@ fn init_textures(args: &Args, images: &mut Assets<Image>) -> Vec<Handle<Image>>
TextureDimension::D2,
pixel,
TextureFormat::Rgba8UnormSrgb,
RenderAssetPersistencePolicy::Unload,
RenderAssetUsages::RENDER_WORLD,
))
})
.collect()